• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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