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