• 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.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