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