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.util.Range; 28 import android.util.RecurrenceRule; 29 30 import com.android.internal.util.Preconditions; 31 32 import java.lang.annotation.Retention; 33 import java.lang.annotation.RetentionPolicy; 34 import java.time.Period; 35 import java.time.ZonedDateTime; 36 import java.util.Iterator; 37 import java.util.Objects; 38 39 /** 40 * Description of a billing relationship plan between a carrier and a specific 41 * subscriber. This information is used to present more useful UI to users, such 42 * as explaining how much mobile data they have remaining, and what will happen 43 * when they run out. 44 * 45 * @see SubscriptionManager#setSubscriptionPlans(int, java.util.List) 46 * @see SubscriptionManager#getSubscriptionPlans(int) 47 */ 48 public final class SubscriptionPlan implements Parcelable { 49 /** {@hide} */ 50 @IntDef(prefix = "LIMIT_BEHAVIOR_", value = { 51 LIMIT_BEHAVIOR_UNKNOWN, 52 LIMIT_BEHAVIOR_DISABLED, 53 LIMIT_BEHAVIOR_BILLED, 54 LIMIT_BEHAVIOR_THROTTLED, 55 }) 56 @Retention(RetentionPolicy.SOURCE) 57 public @interface LimitBehavior {} 58 59 /** When a resource limit is hit, the behavior is unknown. */ 60 public static final int LIMIT_BEHAVIOR_UNKNOWN = -1; 61 /** When a resource limit is hit, access is disabled. */ 62 public static final int LIMIT_BEHAVIOR_DISABLED = 0; 63 /** When a resource limit is hit, the user is billed automatically. */ 64 public static final int LIMIT_BEHAVIOR_BILLED = 1; 65 /** When a resource limit is hit, access is throttled to a slower rate. */ 66 public static final int LIMIT_BEHAVIOR_THROTTLED = 2; 67 68 /** Value indicating a number of bytes is unknown. */ 69 public static final long BYTES_UNKNOWN = -1; 70 /** Value indicating a number of bytes is unlimited. */ 71 public static final long BYTES_UNLIMITED = Long.MAX_VALUE; 72 73 /** Value indicating a timestamp is unknown. */ 74 public static final long TIME_UNKNOWN = -1; 75 76 private final RecurrenceRule cycleRule; 77 private CharSequence title; 78 private CharSequence summary; 79 private long dataLimitBytes = BYTES_UNKNOWN; 80 private int dataLimitBehavior = LIMIT_BEHAVIOR_UNKNOWN; 81 private long dataUsageBytes = BYTES_UNKNOWN; 82 private long dataUsageTime = TIME_UNKNOWN; 83 SubscriptionPlan(RecurrenceRule cycleRule)84 private SubscriptionPlan(RecurrenceRule cycleRule) { 85 this.cycleRule = Preconditions.checkNotNull(cycleRule); 86 } 87 SubscriptionPlan(Parcel source)88 private SubscriptionPlan(Parcel source) { 89 cycleRule = source.readParcelable(null); 90 title = source.readCharSequence(); 91 summary = source.readCharSequence(); 92 dataLimitBytes = source.readLong(); 93 dataLimitBehavior = source.readInt(); 94 dataUsageBytes = source.readLong(); 95 dataUsageTime = source.readLong(); 96 } 97 98 @Override describeContents()99 public int describeContents() { 100 return 0; 101 } 102 103 @Override writeToParcel(Parcel dest, int flags)104 public void writeToParcel(Parcel dest, int flags) { 105 dest.writeParcelable(cycleRule, flags); 106 dest.writeCharSequence(title); 107 dest.writeCharSequence(summary); 108 dest.writeLong(dataLimitBytes); 109 dest.writeInt(dataLimitBehavior); 110 dest.writeLong(dataUsageBytes); 111 dest.writeLong(dataUsageTime); 112 } 113 114 @Override toString()115 public String toString() { 116 return new StringBuilder("SubscriptionPlan{") 117 .append("cycleRule=").append(cycleRule) 118 .append(" title=").append(title) 119 .append(" summary=").append(summary) 120 .append(" dataLimitBytes=").append(dataLimitBytes) 121 .append(" dataLimitBehavior=").append(dataLimitBehavior) 122 .append(" dataUsageBytes=").append(dataUsageBytes) 123 .append(" dataUsageTime=").append(dataUsageTime) 124 .append("}").toString(); 125 } 126 127 @Override hashCode()128 public int hashCode() { 129 return Objects.hash(cycleRule, title, summary, dataLimitBytes, dataLimitBehavior, 130 dataUsageBytes, dataUsageTime); 131 } 132 133 @Override equals(Object obj)134 public boolean equals(Object obj) { 135 if (obj instanceof SubscriptionPlan) { 136 final SubscriptionPlan other = (SubscriptionPlan) obj; 137 return Objects.equals(cycleRule, other.cycleRule) 138 && Objects.equals(title, other.title) 139 && Objects.equals(summary, other.summary) 140 && dataLimitBytes == other.dataLimitBytes 141 && dataLimitBehavior == other.dataLimitBehavior 142 && dataUsageBytes == other.dataUsageBytes 143 && dataUsageTime == other.dataUsageTime; 144 } 145 return false; 146 } 147 148 public static final @android.annotation.NonNull Parcelable.Creator<SubscriptionPlan> CREATOR = new Parcelable.Creator<SubscriptionPlan>() { 149 @Override 150 public SubscriptionPlan createFromParcel(Parcel source) { 151 return new SubscriptionPlan(source); 152 } 153 154 @Override 155 public SubscriptionPlan[] newArray(int size) { 156 return new SubscriptionPlan[size]; 157 } 158 }; 159 160 /** {@hide} */ getCycleRule()161 public @NonNull RecurrenceRule getCycleRule() { 162 return cycleRule; 163 } 164 165 /** Return the short title of this plan. */ getTitle()166 public @Nullable CharSequence getTitle() { 167 return title; 168 } 169 170 /** Return the short summary of this plan. */ getSummary()171 public @Nullable CharSequence getSummary() { 172 return summary; 173 } 174 175 /** 176 * Return the usage threshold at which data access changes according to 177 * {@link #getDataLimitBehavior()}. 178 */ getDataLimitBytes()179 public @BytesLong long getDataLimitBytes() { 180 return dataLimitBytes; 181 } 182 183 /** 184 * Return the behavior of data access when usage reaches 185 * {@link #getDataLimitBytes()}. 186 */ getDataLimitBehavior()187 public @LimitBehavior int getDataLimitBehavior() { 188 return dataLimitBehavior; 189 } 190 191 /** 192 * Return a snapshot of currently known mobile data usage at 193 * {@link #getDataUsageTime()}. 194 */ getDataUsageBytes()195 public @BytesLong long getDataUsageBytes() { 196 return dataUsageBytes; 197 } 198 199 /** 200 * Return the time at which {@link #getDataUsageBytes()} was valid. 201 */ getDataUsageTime()202 public @CurrentTimeMillisLong long getDataUsageTime() { 203 return dataUsageTime; 204 } 205 206 /** 207 * Return an iterator that will return all valid data usage cycles based on 208 * any recurrence rules. The iterator starts from the currently active cycle 209 * and walks backwards through time. 210 */ cycleIterator()211 public Iterator<Range<ZonedDateTime>> cycleIterator() { 212 return cycleRule.cycleIterator(); 213 } 214 215 /** 216 * Builder for a {@link SubscriptionPlan}. 217 */ 218 public static class Builder { 219 private final SubscriptionPlan plan; 220 221 /** {@hide} */ Builder(ZonedDateTime start, ZonedDateTime end, Period period)222 public Builder(ZonedDateTime start, ZonedDateTime end, Period period) { 223 plan = new SubscriptionPlan(new RecurrenceRule(start, end, period)); 224 } 225 226 /** 227 * Start defining a {@link SubscriptionPlan} that covers a very specific 228 * window of time, and never automatically recurs. 229 * 230 * @param start The exact time at which the plan starts. 231 * @param end The exact time at which the plan ends. 232 */ createNonrecurring(ZonedDateTime start, ZonedDateTime end)233 public static Builder createNonrecurring(ZonedDateTime start, ZonedDateTime end) { 234 if (!end.isAfter(start)) { 235 throw new IllegalArgumentException( 236 "End " + end + " isn't after start " + start); 237 } 238 return new Builder(start, end, null); 239 } 240 241 /** 242 * Start defining a {@link SubscriptionPlan} that starts at a specific 243 * time, and automatically recurs after each specific period of time, 244 * repeating indefinitely. 245 * <p> 246 * When the given period is set to exactly one month, the plan will 247 * always recur on the day of the month defined by 248 * {@link ZonedDateTime#getDayOfMonth()}. When a particular month ends 249 * before this day, the plan will recur on the last possible instant of 250 * that month. 251 * 252 * @param start The exact time at which the plan starts. 253 * @param period The period after which the plan automatically recurs. 254 */ createRecurring(ZonedDateTime start, Period period)255 public static Builder createRecurring(ZonedDateTime start, Period period) { 256 if (period.isZero() || period.isNegative()) { 257 throw new IllegalArgumentException("Period " + period + " must be positive"); 258 } 259 return new Builder(start, null, period); 260 } 261 262 /** {@hide} */ 263 @SystemApi 264 @Deprecated createRecurringMonthly(ZonedDateTime start)265 public static Builder createRecurringMonthly(ZonedDateTime start) { 266 return new Builder(start, null, Period.ofMonths(1)); 267 } 268 269 /** {@hide} */ 270 @SystemApi 271 @Deprecated createRecurringWeekly(ZonedDateTime start)272 public static Builder createRecurringWeekly(ZonedDateTime start) { 273 return new Builder(start, null, Period.ofDays(7)); 274 } 275 276 /** {@hide} */ 277 @SystemApi 278 @Deprecated createRecurringDaily(ZonedDateTime start)279 public static Builder createRecurringDaily(ZonedDateTime start) { 280 return new Builder(start, null, Period.ofDays(1)); 281 } 282 build()283 public SubscriptionPlan build() { 284 return plan; 285 } 286 287 /** Set the short title of this plan. */ setTitle(@ullable CharSequence title)288 public Builder setTitle(@Nullable CharSequence title) { 289 plan.title = title; 290 return this; 291 } 292 293 /** Set the short summary of this plan. */ setSummary(@ullable CharSequence summary)294 public Builder setSummary(@Nullable CharSequence summary) { 295 plan.summary = summary; 296 return this; 297 } 298 299 /** 300 * Set the usage threshold at which data access changes. 301 * 302 * @param dataLimitBytes the usage threshold at which data access 303 * changes 304 * @param dataLimitBehavior the behavior of data access when usage 305 * reaches the threshold 306 */ setDataLimit(@ytesLong long dataLimitBytes, @LimitBehavior int dataLimitBehavior)307 public Builder setDataLimit(@BytesLong long dataLimitBytes, 308 @LimitBehavior int dataLimitBehavior) { 309 if (dataLimitBytes < 0) { 310 throw new IllegalArgumentException("Limit bytes must be positive"); 311 } 312 if (dataLimitBehavior < 0) { 313 throw new IllegalArgumentException("Limit behavior must be defined"); 314 } 315 plan.dataLimitBytes = dataLimitBytes; 316 plan.dataLimitBehavior = dataLimitBehavior; 317 return this; 318 } 319 320 /** 321 * Set a snapshot of currently known mobile data usage. 322 * 323 * @param dataUsageBytes the currently known mobile data usage 324 * @param dataUsageTime the time at which this snapshot was valid 325 */ setDataUsage(@ytesLong long dataUsageBytes, @CurrentTimeMillisLong long dataUsageTime)326 public Builder setDataUsage(@BytesLong long dataUsageBytes, 327 @CurrentTimeMillisLong long dataUsageTime) { 328 if (dataUsageBytes < 0) { 329 throw new IllegalArgumentException("Usage bytes must be positive"); 330 } 331 if (dataUsageTime < 0) { 332 throw new IllegalArgumentException("Usage time must be positive"); 333 } 334 plan.dataUsageBytes = dataUsageBytes; 335 plan.dataUsageTime = dataUsageTime; 336 return this; 337 } 338 } 339 } 340