387 lines
14 KiB
Java
387 lines
14 KiB
Java
![]() |
/*
|
||
|
* Copyright (C) 2017 The Android Open Source Project
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
package android.telephony;
|
||
|
|
||
|
import android.annotation.BytesLong;
|
||
|
import android.annotation.CurrentTimeMillisLong;
|
||
|
import android.annotation.IntDef;
|
||
|
import android.annotation.NonNull;
|
||
|
import android.annotation.Nullable;
|
||
|
import android.annotation.SystemApi;
|
||
|
import android.os.Parcel;
|
||
|
import android.os.Parcelable;
|
||
|
import android.telephony.Annotation.NetworkType;
|
||
|
import android.util.Range;
|
||
|
import android.util.RecurrenceRule;
|
||
|
|
||
|
import com.android.internal.util.Preconditions;
|
||
|
|
||
|
import java.lang.annotation.Retention;
|
||
|
import java.lang.annotation.RetentionPolicy;
|
||
|
import java.time.Period;
|
||
|
import java.time.ZonedDateTime;
|
||
|
import java.util.Arrays;
|
||
|
import java.util.Iterator;
|
||
|
import java.util.Objects;
|
||
|
|
||
|
/**
|
||
|
* Description of a billing relationship plan between a carrier and a specific
|
||
|
* subscriber. This information is used to present more useful UI to users, such
|
||
|
* as explaining how much mobile data they have remaining, and what will happen
|
||
|
* when they run out.
|
||
|
*
|
||
|
* If specifying network types, the developer must supply at least one plan
|
||
|
* that applies to all network types (default), and all additional plans
|
||
|
* may not include a particular network type more than once.
|
||
|
* This is enforced by {@link SubscriptionManager} when setting the plans.
|
||
|
*
|
||
|
* Plan selection will prefer plans that have specific network types defined
|
||
|
* over plans that apply to all network types.
|
||
|
*
|
||
|
* @see SubscriptionManager#setSubscriptionPlans(int, java.util.List)
|
||
|
* @see SubscriptionManager#getSubscriptionPlans(int)
|
||
|
*/
|
||
|
public final class SubscriptionPlan implements Parcelable {
|
||
|
/** {@hide} */
|
||
|
@IntDef(prefix = "LIMIT_BEHAVIOR_", value = {
|
||
|
LIMIT_BEHAVIOR_UNKNOWN,
|
||
|
LIMIT_BEHAVIOR_DISABLED,
|
||
|
LIMIT_BEHAVIOR_BILLED,
|
||
|
LIMIT_BEHAVIOR_THROTTLED,
|
||
|
})
|
||
|
@Retention(RetentionPolicy.SOURCE)
|
||
|
public @interface LimitBehavior {}
|
||
|
|
||
|
/** When a resource limit is hit, the behavior is unknown. */
|
||
|
public static final int LIMIT_BEHAVIOR_UNKNOWN = -1;
|
||
|
/** When a resource limit is hit, access is disabled. */
|
||
|
public static final int LIMIT_BEHAVIOR_DISABLED = 0;
|
||
|
/** When a resource limit is hit, the user is billed automatically. */
|
||
|
public static final int LIMIT_BEHAVIOR_BILLED = 1;
|
||
|
/** When a resource limit is hit, access is throttled to a slower rate. */
|
||
|
public static final int LIMIT_BEHAVIOR_THROTTLED = 2;
|
||
|
|
||
|
/** Value indicating a number of bytes is unknown. */
|
||
|
public static final long BYTES_UNKNOWN = -1;
|
||
|
/** Value indicating a number of bytes is unlimited. */
|
||
|
public static final long BYTES_UNLIMITED = Long.MAX_VALUE;
|
||
|
|
||
|
/** Value indicating a timestamp is unknown. */
|
||
|
public static final long TIME_UNKNOWN = -1;
|
||
|
|
||
|
private final RecurrenceRule cycleRule;
|
||
|
private CharSequence title;
|
||
|
private CharSequence summary;
|
||
|
private long dataLimitBytes = BYTES_UNKNOWN;
|
||
|
private int dataLimitBehavior = LIMIT_BEHAVIOR_UNKNOWN;
|
||
|
private long dataUsageBytes = BYTES_UNKNOWN;
|
||
|
private long dataUsageTime = TIME_UNKNOWN;
|
||
|
private @NetworkType int[] networkTypes;
|
||
|
|
||
|
private SubscriptionPlan(RecurrenceRule cycleRule) {
|
||
|
this.cycleRule = Preconditions.checkNotNull(cycleRule);
|
||
|
this.networkTypes = Arrays.copyOf(TelephonyManager.getAllNetworkTypes(),
|
||
|
TelephonyManager.getAllNetworkTypes().length);
|
||
|
}
|
||
|
|
||
|
private SubscriptionPlan(Parcel source) {
|
||
|
cycleRule = source.readParcelable(null, android.util.RecurrenceRule.class);
|
||
|
title = source.readCharSequence();
|
||
|
summary = source.readCharSequence();
|
||
|
dataLimitBytes = source.readLong();
|
||
|
dataLimitBehavior = source.readInt();
|
||
|
dataUsageBytes = source.readLong();
|
||
|
dataUsageTime = source.readLong();
|
||
|
networkTypes = source.createIntArray();
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public int describeContents() {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void writeToParcel(Parcel dest, int flags) {
|
||
|
dest.writeParcelable(cycleRule, flags);
|
||
|
dest.writeCharSequence(title);
|
||
|
dest.writeCharSequence(summary);
|
||
|
dest.writeLong(dataLimitBytes);
|
||
|
dest.writeInt(dataLimitBehavior);
|
||
|
dest.writeLong(dataUsageBytes);
|
||
|
dest.writeLong(dataUsageTime);
|
||
|
dest.writeIntArray(networkTypes);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public String toString() {
|
||
|
return new StringBuilder("SubscriptionPlan{")
|
||
|
.append("cycleRule=").append(cycleRule)
|
||
|
.append(" title=").append(title)
|
||
|
.append(" summary=").append(summary)
|
||
|
.append(" dataLimitBytes=").append(dataLimitBytes)
|
||
|
.append(" dataLimitBehavior=").append(dataLimitBehavior)
|
||
|
.append(" dataUsageBytes=").append(dataUsageBytes)
|
||
|
.append(" dataUsageTime=").append(dataUsageTime)
|
||
|
.append(" networkTypes=").append(Arrays.toString(networkTypes))
|
||
|
.append("}").toString();
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public int hashCode() {
|
||
|
return Objects.hash(cycleRule, title, summary, dataLimitBytes, dataLimitBehavior,
|
||
|
dataUsageBytes, dataUsageTime, Arrays.hashCode(networkTypes));
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public boolean equals(@Nullable Object obj) {
|
||
|
if (obj instanceof SubscriptionPlan) {
|
||
|
final SubscriptionPlan other = (SubscriptionPlan) obj;
|
||
|
return Objects.equals(cycleRule, other.cycleRule)
|
||
|
&& Objects.equals(title, other.title)
|
||
|
&& Objects.equals(summary, other.summary)
|
||
|
&& dataLimitBytes == other.dataLimitBytes
|
||
|
&& dataLimitBehavior == other.dataLimitBehavior
|
||
|
&& dataUsageBytes == other.dataUsageBytes
|
||
|
&& dataUsageTime == other.dataUsageTime
|
||
|
&& Arrays.equals(networkTypes, other.networkTypes);
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
public static final @android.annotation.NonNull Parcelable.Creator<SubscriptionPlan> CREATOR = new Parcelable.Creator<SubscriptionPlan>() {
|
||
|
@Override
|
||
|
public SubscriptionPlan createFromParcel(Parcel source) {
|
||
|
return new SubscriptionPlan(source);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public SubscriptionPlan[] newArray(int size) {
|
||
|
return new SubscriptionPlan[size];
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/** {@hide} */
|
||
|
public @NonNull RecurrenceRule getCycleRule() {
|
||
|
return cycleRule;
|
||
|
}
|
||
|
|
||
|
/** Return the short title of this plan. */
|
||
|
public @Nullable CharSequence getTitle() {
|
||
|
return title;
|
||
|
}
|
||
|
|
||
|
/** Return the short summary of this plan. */
|
||
|
public @Nullable CharSequence getSummary() {
|
||
|
return summary;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return the usage threshold at which data access changes according to
|
||
|
* {@link #getDataLimitBehavior()}.
|
||
|
*/
|
||
|
public @BytesLong long getDataLimitBytes() {
|
||
|
return dataLimitBytes;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return the behavior of data access when usage reaches
|
||
|
* {@link #getDataLimitBytes()}.
|
||
|
*/
|
||
|
public @LimitBehavior int getDataLimitBehavior() {
|
||
|
return dataLimitBehavior;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return a snapshot of currently known mobile data usage at
|
||
|
* {@link #getDataUsageTime()}.
|
||
|
*/
|
||
|
public @BytesLong long getDataUsageBytes() {
|
||
|
return dataUsageBytes;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return the time at which {@link #getDataUsageBytes()} was valid.
|
||
|
*/
|
||
|
public @CurrentTimeMillisLong long getDataUsageTime() {
|
||
|
return dataUsageTime;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return an array containing all network types this SubscriptionPlan applies to.
|
||
|
* @see TelephonyManager for network types values
|
||
|
*/
|
||
|
public @NonNull @NetworkType int[] getNetworkTypes() {
|
||
|
return Arrays.copyOf(networkTypes, networkTypes.length);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return an iterator that will return all valid data usage cycles based on
|
||
|
* any recurrence rules. The iterator starts from the currently active cycle
|
||
|
* and walks backwards through time.
|
||
|
*/
|
||
|
public Iterator<Range<ZonedDateTime>> cycleIterator() {
|
||
|
return cycleRule.cycleIterator();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Builder for a {@link SubscriptionPlan}.
|
||
|
*/
|
||
|
public static class Builder {
|
||
|
private final SubscriptionPlan plan;
|
||
|
|
||
|
/** {@hide} */
|
||
|
public Builder(ZonedDateTime start, ZonedDateTime end, Period period) {
|
||
|
plan = new SubscriptionPlan(new RecurrenceRule(start, end, period));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Start defining a {@link SubscriptionPlan} that covers a very specific
|
||
|
* window of time, and never automatically recurs.
|
||
|
*
|
||
|
* @param start The exact time at which the plan starts.
|
||
|
* @param end The exact time at which the plan ends.
|
||
|
*/
|
||
|
public static Builder createNonrecurring(ZonedDateTime start, ZonedDateTime end) {
|
||
|
if (!end.isAfter(start)) {
|
||
|
throw new IllegalArgumentException(
|
||
|
"End " + end + " isn't after start " + start);
|
||
|
}
|
||
|
return new Builder(start, end, null);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Start defining a {@link SubscriptionPlan} that starts at a specific
|
||
|
* time, and automatically recurs after each specific period of time,
|
||
|
* repeating indefinitely.
|
||
|
* <p>
|
||
|
* When the given period is set to exactly one month, the plan will
|
||
|
* always recur on the day of the month defined by
|
||
|
* {@link ZonedDateTime#getDayOfMonth()}. When a particular month ends
|
||
|
* before this day, the plan will recur on the last possible instant of
|
||
|
* that month.
|
||
|
*
|
||
|
* @param start The exact time at which the plan starts.
|
||
|
* @param period The period after which the plan automatically recurs.
|
||
|
*/
|
||
|
public static Builder createRecurring(ZonedDateTime start, Period period) {
|
||
|
if (period.isZero() || period.isNegative()) {
|
||
|
throw new IllegalArgumentException("Period " + period + " must be positive");
|
||
|
}
|
||
|
return new Builder(start, null, period);
|
||
|
}
|
||
|
|
||
|
/** {@hide} */
|
||
|
@SystemApi
|
||
|
@Deprecated
|
||
|
public static Builder createRecurringMonthly(ZonedDateTime start) {
|
||
|
return new Builder(start, null, Period.ofMonths(1));
|
||
|
}
|
||
|
|
||
|
/** {@hide} */
|
||
|
@SystemApi
|
||
|
@Deprecated
|
||
|
public static Builder createRecurringWeekly(ZonedDateTime start) {
|
||
|
return new Builder(start, null, Period.ofDays(7));
|
||
|
}
|
||
|
|
||
|
/** {@hide} */
|
||
|
@SystemApi
|
||
|
@Deprecated
|
||
|
public static Builder createRecurringDaily(ZonedDateTime start) {
|
||
|
return new Builder(start, null, Period.ofDays(1));
|
||
|
}
|
||
|
|
||
|
public SubscriptionPlan build() {
|
||
|
return plan;
|
||
|
}
|
||
|
|
||
|
/** Set the short title of this plan. */
|
||
|
public Builder setTitle(@Nullable CharSequence title) {
|
||
|
plan.title = title;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/** Set the short summary of this plan. */
|
||
|
public Builder setSummary(@Nullable CharSequence summary) {
|
||
|
plan.summary = summary;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the usage threshold at which data access changes.
|
||
|
*
|
||
|
* @param dataLimitBytes the usage threshold at which data access
|
||
|
* changes
|
||
|
* @param dataLimitBehavior the behavior of data access when usage
|
||
|
* reaches the threshold
|
||
|
*/
|
||
|
public Builder setDataLimit(@BytesLong long dataLimitBytes,
|
||
|
@LimitBehavior int dataLimitBehavior) {
|
||
|
if (dataLimitBytes < 0) {
|
||
|
throw new IllegalArgumentException("Limit bytes must be positive");
|
||
|
}
|
||
|
if (dataLimitBehavior < 0) {
|
||
|
throw new IllegalArgumentException("Limit behavior must be defined");
|
||
|
}
|
||
|
plan.dataLimitBytes = dataLimitBytes;
|
||
|
plan.dataLimitBehavior = dataLimitBehavior;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set a snapshot of currently known mobile data usage.
|
||
|
*
|
||
|
* @param dataUsageBytes the currently known mobile data usage
|
||
|
* @param dataUsageTime the time at which this snapshot was valid
|
||
|
*/
|
||
|
public Builder setDataUsage(@BytesLong long dataUsageBytes,
|
||
|
@CurrentTimeMillisLong long dataUsageTime) {
|
||
|
if (dataUsageBytes < 0) {
|
||
|
throw new IllegalArgumentException("Usage bytes must be positive");
|
||
|
}
|
||
|
if (dataUsageTime < 0) {
|
||
|
throw new IllegalArgumentException("Usage time must be positive");
|
||
|
}
|
||
|
plan.dataUsageBytes = dataUsageBytes;
|
||
|
plan.dataUsageTime = dataUsageTime;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the network types this SubscriptionPlan applies to. By default the plan will apply
|
||
|
* to all network types. An empty array means this plan applies to no network types.
|
||
|
*
|
||
|
* @param networkTypes an array of all network types that apply to this plan.
|
||
|
* @see TelephonyManager for network type values
|
||
|
*/
|
||
|
public @NonNull Builder setNetworkTypes(@NonNull @NetworkType int[] networkTypes) {
|
||
|
plan.networkTypes = Arrays.copyOf(networkTypes, networkTypes.length);
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Reset any network types that were set with {@link #setNetworkTypes(int[])}.
|
||
|
* This will make the SubscriptionPlan apply to all network types.
|
||
|
*/
|
||
|
public @NonNull Builder resetNetworkTypes() {
|
||
|
plan.networkTypes = Arrays.copyOf(TelephonyManager.getAllNetworkTypes(),
|
||
|
TelephonyManager.getAllNetworkTypes().length);
|
||
|
return this;
|
||
|
}
|
||
|
}
|
||
|
}
|