1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.telephony; 18 19 import android.annotation.BytesLong; 20 import android.annotation.CurrentTimeMillisLong; 21 import android.annotation.IntDef; 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.annotation.SystemApi; 25 import android.os.Parcel; 26 import android.os.Parcelable; 27 import android.telephony.Annotation.NetworkType; 28 import android.util.Range; 29 import android.util.RecurrenceRule; 30 31 import com.android.internal.util.Preconditions; 32 33 import java.lang.annotation.Retention; 34 import java.lang.annotation.RetentionPolicy; 35 import java.time.Period; 36 import java.time.ZonedDateTime; 37 import java.util.Arrays; 38 import java.util.Iterator; 39 import java.util.Objects; 40 41 /** 42 * Description of a billing relationship plan between a carrier and a specific 43 * subscriber. This information is used to present more useful UI to users, such 44 * as explaining how much mobile data they have remaining, and what will happen 45 * when they run out. 46 * 47 * If specifying network types, the developer must supply at least one plan 48 * that applies to all network types (default), and all additional plans 49 * may not include a particular network type more than once. 50 * This is enforced by {@link SubscriptionManager} when setting the plans. 51 * 52 * Plan selection will prefer plans that have specific network types defined 53 * over plans that apply to all network types. 54 * 55 * @see SubscriptionManager#setSubscriptionPlans(int, java.util.List) 56 * @see SubscriptionManager#getSubscriptionPlans(int) 57 */ 58 public final class SubscriptionPlan implements Parcelable { 59 /** {@hide} */ 60 @IntDef(prefix = "LIMIT_BEHAVIOR_", value = { 61 LIMIT_BEHAVIOR_UNKNOWN, 62 LIMIT_BEHAVIOR_DISABLED, 63 LIMIT_BEHAVIOR_BILLED, 64 LIMIT_BEHAVIOR_THROTTLED, 65 }) 66 @Retention(RetentionPolicy.SOURCE) 67 public @interface LimitBehavior {} 68 69 /** When a resource limit is hit, the behavior is unknown. */ 70 public static final int LIMIT_BEHAVIOR_UNKNOWN = -1; 71 /** When a resource limit is hit, access is disabled. */ 72 public static final int LIMIT_BEHAVIOR_DISABLED = 0; 73 /** When a resource limit is hit, the user is billed automatically. */ 74 public static final int LIMIT_BEHAVIOR_BILLED = 1; 75 /** When a resource limit is hit, access is throttled to a slower rate. */ 76 public static final int LIMIT_BEHAVIOR_THROTTLED = 2; 77 78 /** Value indicating a number of bytes is unknown. */ 79 public static final long BYTES_UNKNOWN = -1; 80 /** Value indicating a number of bytes is unlimited. */ 81 public static final long BYTES_UNLIMITED = Long.MAX_VALUE; 82 83 /** Value indicating a timestamp is unknown. */ 84 public static final long TIME_UNKNOWN = -1; 85 86 private final RecurrenceRule cycleRule; 87 private CharSequence title; 88 private CharSequence summary; 89 private long dataLimitBytes = BYTES_UNKNOWN; 90 private int dataLimitBehavior = LIMIT_BEHAVIOR_UNKNOWN; 91 private long dataUsageBytes = BYTES_UNKNOWN; 92 private long dataUsageTime = TIME_UNKNOWN; 93 private @NetworkType int[] networkTypes; 94 SubscriptionPlan(RecurrenceRule cycleRule)95 private SubscriptionPlan(RecurrenceRule cycleRule) { 96 this.cycleRule = Preconditions.checkNotNull(cycleRule); 97 this.networkTypes = Arrays.copyOf(TelephonyManager.getAllNetworkTypes(), 98 TelephonyManager.getAllNetworkTypes().length); 99 } 100 SubscriptionPlan(Parcel source)101 private SubscriptionPlan(Parcel source) { 102 cycleRule = source.readParcelable(null); 103 title = source.readCharSequence(); 104 summary = source.readCharSequence(); 105 dataLimitBytes = source.readLong(); 106 dataLimitBehavior = source.readInt(); 107 dataUsageBytes = source.readLong(); 108 dataUsageTime = source.readLong(); 109 networkTypes = source.createIntArray(); 110 } 111 112 @Override describeContents()113 public int describeContents() { 114 return 0; 115 } 116 117 @Override writeToParcel(Parcel dest, int flags)118 public void writeToParcel(Parcel dest, int flags) { 119 dest.writeParcelable(cycleRule, flags); 120 dest.writeCharSequence(title); 121 dest.writeCharSequence(summary); 122 dest.writeLong(dataLimitBytes); 123 dest.writeInt(dataLimitBehavior); 124 dest.writeLong(dataUsageBytes); 125 dest.writeLong(dataUsageTime); 126 dest.writeIntArray(networkTypes); 127 } 128 129 @Override toString()130 public String toString() { 131 return new StringBuilder("SubscriptionPlan{") 132 .append("cycleRule=").append(cycleRule) 133 .append(" title=").append(title) 134 .append(" summary=").append(summary) 135 .append(" dataLimitBytes=").append(dataLimitBytes) 136 .append(" dataLimitBehavior=").append(dataLimitBehavior) 137 .append(" dataUsageBytes=").append(dataUsageBytes) 138 .append(" dataUsageTime=").append(dataUsageTime) 139 .append(" networkTypes=").append(Arrays.toString(networkTypes)) 140 .append("}").toString(); 141 } 142 143 @Override hashCode()144 public int hashCode() { 145 return Objects.hash(cycleRule, title, summary, dataLimitBytes, dataLimitBehavior, 146 dataUsageBytes, dataUsageTime, Arrays.hashCode(networkTypes)); 147 } 148 149 @Override equals(@ullable Object obj)150 public boolean equals(@Nullable Object obj) { 151 if (obj instanceof SubscriptionPlan) { 152 final SubscriptionPlan other = (SubscriptionPlan) obj; 153 return Objects.equals(cycleRule, other.cycleRule) 154 && Objects.equals(title, other.title) 155 && Objects.equals(summary, other.summary) 156 && dataLimitBytes == other.dataLimitBytes 157 && dataLimitBehavior == other.dataLimitBehavior 158 && dataUsageBytes == other.dataUsageBytes 159 && dataUsageTime == other.dataUsageTime 160 && Arrays.equals(networkTypes, other.networkTypes); 161 } 162 return false; 163 } 164 165 public static final @android.annotation.NonNull Parcelable.Creator<SubscriptionPlan> CREATOR = new Parcelable.Creator<SubscriptionPlan>() { 166 @Override 167 public SubscriptionPlan createFromParcel(Parcel source) { 168 return new SubscriptionPlan(source); 169 } 170 171 @Override 172 public SubscriptionPlan[] newArray(int size) { 173 return new SubscriptionPlan[size]; 174 } 175 }; 176 177 /** {@hide} */ getCycleRule()178 public @NonNull RecurrenceRule getCycleRule() { 179 return cycleRule; 180 } 181 182 /** Return the short title of this plan. */ getTitle()183 public @Nullable CharSequence getTitle() { 184 return title; 185 } 186 187 /** Return the short summary of this plan. */ getSummary()188 public @Nullable CharSequence getSummary() { 189 return summary; 190 } 191 192 /** 193 * Return the usage threshold at which data access changes according to 194 * {@link #getDataLimitBehavior()}. 195 */ getDataLimitBytes()196 public @BytesLong long getDataLimitBytes() { 197 return dataLimitBytes; 198 } 199 200 /** 201 * Return the behavior of data access when usage reaches 202 * {@link #getDataLimitBytes()}. 203 */ getDataLimitBehavior()204 public @LimitBehavior int getDataLimitBehavior() { 205 return dataLimitBehavior; 206 } 207 208 /** 209 * Return a snapshot of currently known mobile data usage at 210 * {@link #getDataUsageTime()}. 211 */ getDataUsageBytes()212 public @BytesLong long getDataUsageBytes() { 213 return dataUsageBytes; 214 } 215 216 /** 217 * Return the time at which {@link #getDataUsageBytes()} was valid. 218 */ getDataUsageTime()219 public @CurrentTimeMillisLong long getDataUsageTime() { 220 return dataUsageTime; 221 } 222 223 /** 224 * Return an array containing all {@link NetworkType}s this SubscriptionPlan applies to. 225 * @see TelephonyManager for network types values 226 */ getNetworkTypes()227 public @NonNull @NetworkType int[] getNetworkTypes() { 228 return Arrays.copyOf(networkTypes, networkTypes.length); 229 } 230 231 /** 232 * Return an iterator that will return all valid data usage cycles based on 233 * any recurrence rules. The iterator starts from the currently active cycle 234 * and walks backwards through time. 235 */ cycleIterator()236 public Iterator<Range<ZonedDateTime>> cycleIterator() { 237 return cycleRule.cycleIterator(); 238 } 239 240 /** 241 * Builder for a {@link SubscriptionPlan}. 242 */ 243 public static class Builder { 244 private final SubscriptionPlan plan; 245 246 /** {@hide} */ Builder(ZonedDateTime start, ZonedDateTime end, Period period)247 public Builder(ZonedDateTime start, ZonedDateTime end, Period period) { 248 plan = new SubscriptionPlan(new RecurrenceRule(start, end, period)); 249 } 250 251 /** 252 * Start defining a {@link SubscriptionPlan} that covers a very specific 253 * window of time, and never automatically recurs. 254 * 255 * @param start The exact time at which the plan starts. 256 * @param end The exact time at which the plan ends. 257 */ createNonrecurring(ZonedDateTime start, ZonedDateTime end)258 public static Builder createNonrecurring(ZonedDateTime start, ZonedDateTime end) { 259 if (!end.isAfter(start)) { 260 throw new IllegalArgumentException( 261 "End " + end + " isn't after start " + start); 262 } 263 return new Builder(start, end, null); 264 } 265 266 /** 267 * Start defining a {@link SubscriptionPlan} that starts at a specific 268 * time, and automatically recurs after each specific period of time, 269 * repeating indefinitely. 270 * <p> 271 * When the given period is set to exactly one month, the plan will 272 * always recur on the day of the month defined by 273 * {@link ZonedDateTime#getDayOfMonth()}. When a particular month ends 274 * before this day, the plan will recur on the last possible instant of 275 * that month. 276 * 277 * @param start The exact time at which the plan starts. 278 * @param period The period after which the plan automatically recurs. 279 */ createRecurring(ZonedDateTime start, Period period)280 public static Builder createRecurring(ZonedDateTime start, Period period) { 281 if (period.isZero() || period.isNegative()) { 282 throw new IllegalArgumentException("Period " + period + " must be positive"); 283 } 284 return new Builder(start, null, period); 285 } 286 287 /** {@hide} */ 288 @SystemApi 289 @Deprecated createRecurringMonthly(ZonedDateTime start)290 public static Builder createRecurringMonthly(ZonedDateTime start) { 291 return new Builder(start, null, Period.ofMonths(1)); 292 } 293 294 /** {@hide} */ 295 @SystemApi 296 @Deprecated createRecurringWeekly(ZonedDateTime start)297 public static Builder createRecurringWeekly(ZonedDateTime start) { 298 return new Builder(start, null, Period.ofDays(7)); 299 } 300 301 /** {@hide} */ 302 @SystemApi 303 @Deprecated createRecurringDaily(ZonedDateTime start)304 public static Builder createRecurringDaily(ZonedDateTime start) { 305 return new Builder(start, null, Period.ofDays(1)); 306 } 307 build()308 public SubscriptionPlan build() { 309 return plan; 310 } 311 312 /** Set the short title of this plan. */ setTitle(@ullable CharSequence title)313 public Builder setTitle(@Nullable CharSequence title) { 314 plan.title = title; 315 return this; 316 } 317 318 /** Set the short summary of this plan. */ setSummary(@ullable CharSequence summary)319 public Builder setSummary(@Nullable CharSequence summary) { 320 plan.summary = summary; 321 return this; 322 } 323 324 /** 325 * Set the usage threshold at which data access changes. 326 * 327 * @param dataLimitBytes the usage threshold at which data access 328 * changes 329 * @param dataLimitBehavior the behavior of data access when usage 330 * reaches the threshold 331 */ setDataLimit(@ytesLong long dataLimitBytes, @LimitBehavior int dataLimitBehavior)332 public Builder setDataLimit(@BytesLong long dataLimitBytes, 333 @LimitBehavior int dataLimitBehavior) { 334 if (dataLimitBytes < 0) { 335 throw new IllegalArgumentException("Limit bytes must be positive"); 336 } 337 if (dataLimitBehavior < 0) { 338 throw new IllegalArgumentException("Limit behavior must be defined"); 339 } 340 plan.dataLimitBytes = dataLimitBytes; 341 plan.dataLimitBehavior = dataLimitBehavior; 342 return this; 343 } 344 345 /** 346 * Set a snapshot of currently known mobile data usage. 347 * 348 * @param dataUsageBytes the currently known mobile data usage 349 * @param dataUsageTime the time at which this snapshot was valid 350 */ setDataUsage(@ytesLong long dataUsageBytes, @CurrentTimeMillisLong long dataUsageTime)351 public Builder setDataUsage(@BytesLong long dataUsageBytes, 352 @CurrentTimeMillisLong long dataUsageTime) { 353 if (dataUsageBytes < 0) { 354 throw new IllegalArgumentException("Usage bytes must be positive"); 355 } 356 if (dataUsageTime < 0) { 357 throw new IllegalArgumentException("Usage time must be positive"); 358 } 359 plan.dataUsageBytes = dataUsageBytes; 360 plan.dataUsageTime = dataUsageTime; 361 return this; 362 } 363 364 /** 365 * Set the network types this SubscriptionPlan applies to. By default the plan will apply 366 * to all network types. An empty array means this plan applies to no network types. 367 * 368 * @param networkTypes an array of all {@link NetworkType}s that apply to this plan. 369 * @see TelephonyManager for network type values 370 */ setNetworkTypes(@onNull @etworkType int[] networkTypes)371 public @NonNull Builder setNetworkTypes(@NonNull @NetworkType int[] networkTypes) { 372 plan.networkTypes = Arrays.copyOf(networkTypes, networkTypes.length); 373 return this; 374 } 375 376 /** 377 * Reset any network types that were set with {@link #setNetworkTypes(int[])}. 378 * This will make the SubscriptionPlan apply to all network types. 379 */ resetNetworkTypes()380 public @NonNull Builder resetNetworkTypes() { 381 plan.networkTypes = Arrays.copyOf(TelephonyManager.getAllNetworkTypes(), 382 TelephonyManager.getAllNetworkTypes().length); 383 return this; 384 } 385 } 386 } 387