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