}
public final class SubscriptionPlan implements android.os.Parcelable {
- method public java.util.Iterator<android.util.Pair<java.time.ZonedDateTime, java.time.ZonedDateTime>> cycleIterator();
+ method public java.util.Iterator<android.util.Range<java.time.ZonedDateTime>> cycleIterator();
method public int describeContents();
method public int getDataLimitBehavior();
method public long getDataLimitBytes();
public static class SubscriptionPlan.Builder {
method public android.telephony.SubscriptionPlan build();
method public static android.telephony.SubscriptionPlan.Builder createNonrecurring(java.time.ZonedDateTime, java.time.ZonedDateTime);
- method public static android.telephony.SubscriptionPlan.Builder createRecurringDaily(java.time.ZonedDateTime);
- method public static android.telephony.SubscriptionPlan.Builder createRecurringMonthly(java.time.ZonedDateTime);
- method public static android.telephony.SubscriptionPlan.Builder createRecurringWeekly(java.time.ZonedDateTime);
+ method public static android.telephony.SubscriptionPlan.Builder createRecurring(java.time.ZonedDateTime, java.time.Period);
method public android.telephony.SubscriptionPlan.Builder setDataLimit(long, int);
method public android.telephony.SubscriptionPlan.Builder setDataUsage(long, long);
method public android.telephony.SubscriptionPlan.Builder setSummary(java.lang.CharSequence);
}
public final class SubscriptionPlan implements android.os.Parcelable {
- method public java.util.Iterator<android.util.Pair<java.time.ZonedDateTime, java.time.ZonedDateTime>> cycleIterator();
+ method public java.util.Iterator<android.util.Range<java.time.ZonedDateTime>> cycleIterator();
method public int describeContents();
method public int getDataLimitBehavior();
method public long getDataLimitBytes();
public static class SubscriptionPlan.Builder {
method public android.telephony.SubscriptionPlan build();
method public static android.telephony.SubscriptionPlan.Builder createNonrecurring(java.time.ZonedDateTime, java.time.ZonedDateTime);
- method public static android.telephony.SubscriptionPlan.Builder createRecurringDaily(java.time.ZonedDateTime);
- method public static android.telephony.SubscriptionPlan.Builder createRecurringMonthly(java.time.ZonedDateTime);
- method public static android.telephony.SubscriptionPlan.Builder createRecurringWeekly(java.time.ZonedDateTime);
+ method public static android.telephony.SubscriptionPlan.Builder createRecurring(java.time.ZonedDateTime, java.time.Period);
+ method public static deprecated android.telephony.SubscriptionPlan.Builder createRecurringDaily(java.time.ZonedDateTime);
+ method public static deprecated android.telephony.SubscriptionPlan.Builder createRecurringMonthly(java.time.ZonedDateTime);
+ method public static deprecated android.telephony.SubscriptionPlan.Builder createRecurringWeekly(java.time.ZonedDateTime);
method public android.telephony.SubscriptionPlan.Builder setDataLimit(long, int);
method public android.telephony.SubscriptionPlan.Builder setDataUsage(long, long);
method public android.telephony.SubscriptionPlan.Builder setSummary(java.lang.CharSequence);
import android.os.Parcel;
import android.os.Parcelable;
import android.util.BackupUtils;
-import android.util.Pair;
+import android.util.Range;
import android.util.RecurrenceRule;
import com.android.internal.util.Preconditions;
return 0;
}
- public Iterator<Pair<ZonedDateTime, ZonedDateTime>> cycleIterator() {
+ public Iterator<Range<ZonedDateTime>> cycleIterator() {
return cycleRule.cycleIterator();
}
import android.os.UserHandle;
import android.util.DebugUtils;
import android.util.Pair;
+import android.util.Range;
import com.google.android.collect.Sets;
}
/** {@hide} */
+ @Deprecated
public static Iterator<Pair<ZonedDateTime, ZonedDateTime>> cycleIterator(NetworkPolicy policy) {
- return policy.cycleIterator();
+ final Iterator<Range<ZonedDateTime>> it = policy.cycleIterator();
+ return new Iterator<Pair<ZonedDateTime, ZonedDateTime>>() {
+ @Override
+ public boolean hasNext() {
+ return it.hasNext();
+ }
+
+ @Override
+ public Pair<ZonedDateTime, ZonedDateTime> next() {
+ final Range<ZonedDateTime> r = it.next();
+ return Pair.create(r.getLower(), r.getUpper());
+ }
+ };
}
/**
&& period.getDays() == 0;
}
- public Iterator<Pair<ZonedDateTime, ZonedDateTime>> cycleIterator() {
+ public Iterator<Range<ZonedDateTime>> cycleIterator() {
if (period != null) {
return new RecurringIterator();
} else {
}
}
- private class NonrecurringIterator implements Iterator<Pair<ZonedDateTime, ZonedDateTime>> {
+ private class NonrecurringIterator implements Iterator<Range<ZonedDateTime>> {
boolean hasNext;
public NonrecurringIterator() {
}
@Override
- public Pair<ZonedDateTime, ZonedDateTime> next() {
+ public Range<ZonedDateTime> next() {
hasNext = false;
- return new Pair<>(start, end);
+ return new Range<>(start, end);
}
}
- private class RecurringIterator implements Iterator<Pair<ZonedDateTime, ZonedDateTime>> {
+ private class RecurringIterator implements Iterator<Range<ZonedDateTime>> {
int i;
ZonedDateTime cycleStart;
ZonedDateTime cycleEnd;
}
@Override
- public Pair<ZonedDateTime, ZonedDateTime> next() {
+ public Range<ZonedDateTime> next() {
if (LOGD) Log.d(TAG, "Cycle " + i + " from " + cycleStart + " to " + cycleEnd);
- Pair<ZonedDateTime, ZonedDateTime> p = new Pair<>(cycleStart, cycleEnd);
+ Range<ZonedDateTime> r = new Range<>(cycleStart, cycleEnd);
i--;
updateCycle();
- return p;
+ return r;
}
}
<protected-broadcast android:name="android.telephony.action.DEFAULT_SUBSCRIPTION_CHANGED" />
<protected-broadcast android:name="android.telephony.action.DEFAULT_SMS_SUBSCRIPTION_CHANGED" />
<protected-broadcast android:name="android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION" />
+ <protected-broadcast android:name="android.telephony.action.SUBSCRIPTION_PLANS_CHANGED" />
<protected-broadcast android:name="com.android.bluetooth.btservice.action.ALARM_WAKEUP" />
<protected-broadcast android:name="com.android.server.action.NETWORK_STATS_POLL" />
assertTrue(r.isMonthly());
- final Iterator<Pair<ZonedDateTime, ZonedDateTime>> it = r.cycleIterator();
+ final Iterator<Range<ZonedDateTime>> it = r.cycleIterator();
assertTrue(it.hasNext());
- assertEquals(Pair.create(
+ assertEquals(new Range<>(
ZonedDateTime.parse("2015-11-14T00:00:00.00Z"),
ZonedDateTime.parse("2015-12-14T00:00:00.00Z")), it.next());
assertTrue(it.hasNext());
- assertEquals(Pair.create(
+ assertEquals(new Range<>(
ZonedDateTime.parse("2015-10-14T00:00:00.00Z"),
ZonedDateTime.parse("2015-11-14T00:00:00.00Z")), it.next());
}
assertFalse(r.isMonthly());
- final Iterator<Pair<ZonedDateTime, ZonedDateTime>> it = r.cycleIterator();
+ final Iterator<Range<ZonedDateTime>> it = r.cycleIterator();
assertTrue(it.hasNext());
- assertEquals(Pair.create(
+ assertEquals(new Range<>(
ZonedDateTime.parse("2010-11-17T00:11:00.00Z"),
ZonedDateTime.parse("2010-11-20T00:11:00.00Z")), it.next());
assertTrue(it.hasNext());
- assertEquals(Pair.create(
+ assertEquals(new Range<>(
ZonedDateTime.parse("2010-11-14T00:11:00.00Z"),
ZonedDateTime.parse("2010-11-17T00:11:00.00Z")), it.next());
assertFalse(it.hasNext());
assertFalse(r.isMonthly());
- final Iterator<Pair<ZonedDateTime, ZonedDateTime>> it = r.cycleIterator();
+ final Iterator<Range<ZonedDateTime>> it = r.cycleIterator();
assertTrue(it.hasNext());
- assertEquals(Pair.create(
+ assertEquals(new Range<>(
ZonedDateTime.parse("2010-11-14T00:11:00.000Z"),
ZonedDateTime.parse("2010-11-20T00:11:00.000Z")), it.next());
assertFalse(it.hasNext());
assertFalse(r.isMonthly());
- final Iterator<Pair<ZonedDateTime, ZonedDateTime>> it = r.cycleIterator();
+ final Iterator<Range<ZonedDateTime>> it = r.cycleIterator();
assertFalse(it.hasNext());
}
ZonedDateTime.parse("2030-01-31T00:00:00.000Z"),
Period.ofMonths(1));
- final Iterator<Pair<ZonedDateTime, ZonedDateTime>> it = r.cycleIterator();
+ final Iterator<Range<ZonedDateTime>> it = r.cycleIterator();
ZonedDateTime lastStart = null;
int months = 0;
while (it.hasNext()) {
- final Pair<ZonedDateTime, ZonedDateTime> cycle = it.next();
+ final Range<ZonedDateTime> cycle = it.next();
// Make sure cycle has reasonable length
- final long length = cycle.second.toEpochSecond() - cycle.first.toEpochSecond();
+ final long length = cycle.getUpper().toEpochSecond() - cycle.getLower().toEpochSecond();
assertTrue(cycle + " must be more than 4 weeks", length >= 2419200);
assertTrue(cycle + " must be less than 5 weeks", length <= 3024000);
// Make sure we have no gaps
if (lastStart != null) {
- assertEquals(lastStart, cycle.second);
+ assertEquals(lastStart, cycle.getUpper());
}
- lastStart = cycle.first;
+ lastStart = cycle.getLower();
months++;
}
import android.telephony.TelephonyManager;
import android.util.DebugUtils;
import android.util.Pair;
+import android.util.Range;
import android.util.Slog;
import com.android.internal.R;
}
private long getRemainingDailyBudget(long limitBytes,
- Pair<ZonedDateTime, ZonedDateTime> cycle) {
- final long start = cycle.first.toInstant().toEpochMilli();
- final long end = cycle.second.toInstant().toEpochMilli();
+ Range<ZonedDateTime> cycle) {
+ final long start = cycle.getLower().toInstant().toEpochMilli();
+ final long end = cycle.getUpper().toInstant().toEpochMilli();
final long totalBytes = getNetworkTotalBytes(start, end);
final long remainingBytes = totalBytes == -1 ? 0 : Math.max(0, limitBytes - totalBytes);
// 1 + ((end - now - 1) / millisInDay with integers is equivalent to:
private static boolean hasActiveCycle(NetworkPolicy policy) {
return policy.hasCycle() && policy.lastLimitSnooze <
- policy.cycleIterator().next().first.toInstant().toEpochMilli();
+ policy.cycleIterator().next().getLower().toInstant().toEpochMilli();
}
// Only ever updated on the handler thread. Accessed from other binder threads to retrieve
import android.util.DataUnit;
import android.util.Log;
import android.util.Pair;
+import android.util.Range;
import android.util.RecurrenceRule;
import android.util.Slog;
import android.util.SparseArray;
quotaBytes = DataUnit.MEBIBYTES.toBytes(20);
} else {
// Limited data; let's only use 10% of remaining budget
- final Pair<ZonedDateTime, ZonedDateTime> cycle = plan.cycleIterator().next();
- final long start = cycle.first.toInstant().toEpochMilli();
- final long end = cycle.second.toInstant().toEpochMilli();
+ final Range<ZonedDateTime> cycle = plan.cycleIterator().next();
+ final long start = cycle.getLower().toInstant().toEpochMilli();
+ final long end = cycle.getUpper().toInstant().toEpochMilli();
final Instant now = mClock.instant();
- final long startOfDay = ZonedDateTime.ofInstant(now, cycle.first.getZone())
+ final long startOfDay = ZonedDateTime.ofInstant(now, cycle.getLower().getZone())
.truncatedTo(ChronoUnit.DAYS)
.toInstant().toEpochMilli();
final long totalBytes = getTotalBytes(
import android.util.AtomicFile;
import android.util.IntArray;
import android.util.MathUtils;
-import android.util.Pair;
+import android.util.Range;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
long collectEnd = end;
if (augmentEnd != SubscriptionPlan.TIME_UNKNOWN) {
- final Iterator<Pair<ZonedDateTime, ZonedDateTime>> it = augmentPlan.cycleIterator();
+ final Iterator<Range<ZonedDateTime>> it = augmentPlan.cycleIterator();
while (it.hasNext()) {
- final Pair<ZonedDateTime, ZonedDateTime> cycle = it.next();
- final long cycleStart = cycle.first.toInstant().toEpochMilli();
- final long cycleEnd = cycle.second.toInstant().toEpochMilli();
+ final Range<ZonedDateTime> cycle = it.next();
+ final long cycleStart = cycle.getLower().toInstant().toEpochMilli();
+ final long cycleEnd = cycle.getUpper().toInstant().toEpochMilli();
if (cycleStart <= augmentEnd && augmentEnd < cycleEnd) {
augmentStart = cycleStart;
collectStart = Long.min(collectStart, augmentStart);
import android.text.format.Time;
import android.util.DataUnit;
import android.util.Log;
-import android.util.Pair;
+import android.util.Range;
import android.util.RecurrenceRule;
import com.android.internal.telephony.PhoneConstants;
private static long computeLastCycleBoundary(long currentTime, NetworkPolicy policy) {
RecurrenceRule.sClock = Clock.fixed(Instant.ofEpochMilli(currentTime),
ZoneId.systemDefault());
- final Iterator<Pair<ZonedDateTime, ZonedDateTime>> it = policy.cycleIterator();
+ final Iterator<Range<ZonedDateTime>> it = policy.cycleIterator();
while (it.hasNext()) {
- final Pair<ZonedDateTime, ZonedDateTime> cycle = it.next();
- if (cycle.first.toInstant().toEpochMilli() < currentTime) {
- return cycle.first.toInstant().toEpochMilli();
+ final Range<ZonedDateTime> cycle = it.next();
+ if (cycle.getLower().toInstant().toEpochMilli() < currentTime) {
+ return cycle.getLower().toInstant().toEpochMilli();
}
}
throw new IllegalStateException(
private static long computeNextCycleBoundary(long currentTime, NetworkPolicy policy) {
RecurrenceRule.sClock = Clock.fixed(Instant.ofEpochMilli(currentTime),
ZoneId.systemDefault());
- return policy.cycleIterator().next().second.toInstant().toEpochMilli();
+ return policy.cycleIterator().next().getUpper().toInstant().toEpochMilli();
}
@Test
* <p>
* Contains {@link #EXTRA_SUBSCRIPTION_INDEX} to indicate which subscription
* the user is interested in.
+ * <p>
+ * Receivers should protect themselves by checking that the sender holds the
+ * {@code android.permission.MANAGE_SUBSCRIPTION_PLANS} permission.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@SystemApi
* </ul>
*
* @param subId the subscriber this relationship applies to
+ * @throws SecurityException if the caller doesn't meet the requirements
+ * outlined above.
*/
@SystemApi
public @NonNull List<SubscriptionPlan> getSubscriptionPlans(int subId) {
* {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}.
* </ul>
*
- * @param subId the subscriber this relationship applies to
+ * @param subId the subscriber this relationship applies to. An empty list
+ * may be sent to clear any existing plans.
* @param plans the list of plans. The first plan is always the primary and
* most important plan. Any additional plans are secondary and
* may not be displayed or used by decision making logic.
+ * @throws SecurityException if the caller doesn't meet the requirements
+ * outlined above.
*/
@SystemApi
public void setSubscriptionPlans(int subId, @NonNull List<SubscriptionPlan> plans) {
* be automatically cleared, or {@code 0} to leave in the
* requested state until explicitly cleared, or the next reboot,
* whichever happens first.
+ * @throws SecurityException if the caller doesn't meet the requirements
+ * outlined above.
*/
@SystemApi
public void setSubscriptionOverrideUnmetered(int subId, boolean overrideUnmetered,
* be automatically cleared, or {@code 0} to leave in the
* requested state until explicitly cleared, or the next reboot,
* whichever happens first.
+ * @throws SecurityException if the caller doesn't meet the requirements
+ * outlined above.
*/
@SystemApi
public void setSubscriptionOverrideCongested(int subId, boolean overrideCongested,
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
-import android.util.Pair;
+import android.util.Range;
import android.util.RecurrenceRule;
import com.android.internal.util.Preconditions;
* any recurrence rules. The iterator starts from the currently active cycle
* and walks backwards through time.
*/
- public Iterator<Pair<ZonedDateTime, ZonedDateTime>> cycleIterator() {
+ public Iterator<Range<ZonedDateTime>> cycleIterator() {
return cycleRule.cycleIterator();
}
/**
* 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)) {
}
/**
- * Start defining a {@link SubscriptionPlan} that will recur
- * automatically every month. It will always recur on the same day of a
- * particular month. When a particular month ends before the defined
- * recurrence day, the plan will recur on the last instant of that
- * month.
+ * 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) {
+ return new Builder(start, null, period);
+ }
+
+ /** {@hide} */
+ @SystemApi
+ @Deprecated
public static Builder createRecurringMonthly(ZonedDateTime start) {
return new Builder(start, null, Period.ofMonths(1));
}
- /**
- * Start defining a {@link SubscriptionPlan} that will recur
- * automatically every week.
- */
+ /** {@hide} */
+ @SystemApi
+ @Deprecated
public static Builder createRecurringWeekly(ZonedDateTime start) {
return new Builder(start, null, Period.ofDays(7));
}
- /**
- * Start defining a {@link SubscriptionPlan} that will recur
- * automatically every day.
- */
+ /** {@hide} */
+ @SystemApi
+ @Deprecated
public static Builder createRecurringDaily(ZonedDateTime start) {
return new Builder(start, null, Period.ofDays(1));
}