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 17package android.telephony; 18 19import android.annotation.BytesLong; 20import android.annotation.CurrentTimeMillisLong; 21import android.annotation.IntDef; 22import android.annotation.NonNull; 23import android.annotation.Nullable; 24import android.annotation.SystemApi; 25import android.os.Parcel; 26import android.os.Parcelable; 27import android.util.Pair; 28import android.util.RecurrenceRule; 29 30import com.android.internal.util.Preconditions; 31 32import java.lang.annotation.Retention; 33import java.lang.annotation.RetentionPolicy; 34import java.time.Period; 35import java.time.ZonedDateTime; 36import java.util.Iterator; 37 38/** 39 * Description of a billing relationship plan between a carrier and a specific 40 * subscriber. This information is used to present more useful UI to users, such 41 * as explaining how much mobile data they have remaining, and what will happen 42 * when they run out. 43 * 44 * @see SubscriptionManager#setSubscriptionPlans(int, java.util.List) 45 * @see SubscriptionManager#getSubscriptionPlans(int) 46 * @hide 47 */ 48@SystemApi 49public 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 85 private SubscriptionPlan(RecurrenceRule cycleRule) { 86 this.cycleRule = Preconditions.checkNotNull(cycleRule); 87 } 88 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 100 public int describeContents() { 101 return 0; 102 } 103 104 @Override 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 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 public static final Parcelable.Creator<SubscriptionPlan> CREATOR = new Parcelable.Creator<SubscriptionPlan>() { 129 @Override 130 public SubscriptionPlan createFromParcel(Parcel source) { 131 return new SubscriptionPlan(source); 132 } 133 134 @Override 135 public SubscriptionPlan[] newArray(int size) { 136 return new SubscriptionPlan[size]; 137 } 138 }; 139 140 /** {@hide} */ 141 public @NonNull RecurrenceRule getCycleRule() { 142 return cycleRule; 143 } 144 145 /** Return the short title of this plan. */ 146 public @Nullable CharSequence getTitle() { 147 return title; 148 } 149 150 /** Return the short summary of this plan. */ 151 public @Nullable CharSequence getSummary() { 152 return summary; 153 } 154 155 /** 156 * Return the usage threshold at which data access changes according to 157 * {@link #getDataLimitBehavior()}. 158 */ 159 public @BytesLong long getDataLimitBytes() { 160 return dataLimitBytes; 161 } 162 163 /** 164 * Return the behavior of data access when usage reaches 165 * {@link #getDataLimitBytes()}. 166 */ 167 public @LimitBehavior int getDataLimitBehavior() { 168 return dataLimitBehavior; 169 } 170 171 /** 172 * Return a snapshot of currently known mobile data usage at 173 * {@link #getDataUsageTime()}. 174 */ 175 public @BytesLong long getDataUsageBytes() { 176 return dataUsageBytes; 177 } 178 179 /** 180 * Return the time at which {@link #getDataUsageBytes()} was valid. 181 */ 182 public @CurrentTimeMillisLong long getDataUsageTime() { 183 return dataUsageTime; 184 } 185 186 /** 187 * Return an iterator that will return all valid data usage cycles based on 188 * any recurrence rules. The iterator starts from the currently active cycle 189 * and walks backwards through time. 190 */ 191 public Iterator<Pair<ZonedDateTime, ZonedDateTime>> cycleIterator() { 192 return cycleRule.cycleIterator(); 193 } 194 195 /** 196 * Builder for a {@link SubscriptionPlan}. 197 */ 198 public static class Builder { 199 private final SubscriptionPlan plan; 200 201 /** {@hide} */ 202 public Builder(ZonedDateTime start, ZonedDateTime end, Period period) { 203 plan = new SubscriptionPlan(new RecurrenceRule(start, end, period)); 204 } 205 206 /** 207 * Start defining a {@link SubscriptionPlan} that covers a very specific 208 * window of time, and never automatically recurs. 209 */ 210 public static Builder createNonrecurring(ZonedDateTime start, ZonedDateTime end) { 211 if (!end.isAfter(start)) { 212 throw new IllegalArgumentException( 213 "End " + end + " isn't after start " + start); 214 } 215 return new Builder(start, end, null); 216 } 217 218 /** 219 * Start defining a {@link SubscriptionPlan} that will recur 220 * automatically every month. It will always recur on the same day of a 221 * particular month. When a particular month ends before the defined 222 * recurrence day, the plan will recur on the last instant of that 223 * month. 224 */ 225 public static Builder createRecurringMonthly(ZonedDateTime start) { 226 return new Builder(start, null, Period.ofMonths(1)); 227 } 228 229 /** 230 * Start defining a {@link SubscriptionPlan} that will recur 231 * automatically every week. 232 */ 233 public static Builder createRecurringWeekly(ZonedDateTime start) { 234 return new Builder(start, null, Period.ofDays(7)); 235 } 236 237 /** 238 * Start defining a {@link SubscriptionPlan} that will recur 239 * automatically every day. 240 */ 241 public static Builder createRecurringDaily(ZonedDateTime start) { 242 return new Builder(start, null, Period.ofDays(1)); 243 } 244 245 public SubscriptionPlan build() { 246 return plan; 247 } 248 249 /** Set the short title of this plan. */ 250 public Builder setTitle(@Nullable CharSequence title) { 251 plan.title = title; 252 return this; 253 } 254 255 /** Set the short summary of this plan. */ 256 public Builder setSummary(@Nullable CharSequence summary) { 257 plan.summary = summary; 258 return this; 259 } 260 261 /** 262 * Set the usage threshold at which data access changes. 263 * 264 * @param dataLimitBytes the usage threshold at which data access 265 * changes 266 * @param dataLimitBehavior the behavior of data access when usage 267 * reaches the threshold 268 */ 269 public Builder setDataLimit(@BytesLong long dataLimitBytes, 270 @LimitBehavior int dataLimitBehavior) { 271 if (dataLimitBytes < 0) { 272 throw new IllegalArgumentException("Limit bytes must be positive"); 273 } 274 if (dataLimitBehavior < 0) { 275 throw new IllegalArgumentException("Limit behavior must be defined"); 276 } 277 plan.dataLimitBytes = dataLimitBytes; 278 plan.dataLimitBehavior = dataLimitBehavior; 279 return this; 280 } 281 282 /** 283 * Set a snapshot of currently known mobile data usage. 284 * 285 * @param dataUsageBytes the currently known mobile data usage 286 * @param dataUsageTime the time at which this snapshot was valid 287 */ 288 public Builder setDataUsage(@BytesLong long dataUsageBytes, 289 @CurrentTimeMillisLong long dataUsageTime) { 290 if (dataUsageBytes < 0) { 291 throw new IllegalArgumentException("Usage bytes must be positive"); 292 } 293 if (dataUsageTime < 0) { 294 throw new IllegalArgumentException("Usage time must be positive"); 295 } 296 plan.dataUsageBytes = dataUsageBytes; 297 plan.dataUsageTime = dataUsageTime; 298 return this; 299 } 300 } 301} 302