Merge "Expose SubscriptionPlan API." into oc-mr1-dev

am: 84c6684bba

Change-Id: I9e05d8ae075c698bc4c19132df92ea8f3f3fc52b
This commit is contained in:
Jeff Sharkey
2017-07-21 17:22:26 +00:00
committed by android-build-merger
17 changed files with 978 additions and 545 deletions

View File

@@ -39536,7 +39536,7 @@ package android.telephony {
method public android.os.PersistableBundle getConfigForSubId(int);
method public void notifyConfigChangedForSubId(int);
field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
field public static final int DATA_CYCLE_THRESHOLD_DISABLED = -2; // 0xfffffffe
field public static final deprecated int DATA_CYCLE_THRESHOLD_DISABLED = -2; // 0xfffffffe
field public static final java.lang.String KEY_ADDITIONAL_CALL_SETTING_BOOL = "additional_call_setting_bool";
field public static final java.lang.String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool";
field public static final java.lang.String KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL = "allow_add_call_during_video_call";
@@ -39578,9 +39578,10 @@ package android.telephony {
field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING = "ci_action_on_sys_update_extra_val_string";
field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING = "ci_action_on_sys_update_intent_string";
field public static final java.lang.String KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING = "config_ims_package_override_string";
field public static final java.lang.String KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING = "config_plans_package_override_string";
field public static final java.lang.String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool";
field public static final java.lang.String KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG = "data_limit_threshold_bytes_long";
field public static final java.lang.String KEY_DATA_WARNING_THRESHOLD_BYTES_LONG = "data_warning_threshold_bytes_long";
field public static final deprecated java.lang.String KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG = "data_limit_threshold_bytes_long";
field public static final deprecated java.lang.String KEY_DATA_WARNING_THRESHOLD_BYTES_LONG = "data_warning_threshold_bytes_long";
field public static final java.lang.String KEY_DEFAULT_SIM_CALL_MANAGER_STRING = "default_sim_call_manager_string";
field public static final java.lang.String KEY_DEFAULT_VM_NUMBER_STRING = "default_vm_number_string";
field public static final java.lang.String KEY_DIAL_STRING_REPLACE_STRING_ARRAY = "dial_string_replace_string_array";
@@ -39637,7 +39638,7 @@ package android.telephony {
field public static final java.lang.String KEY_MMS_UA_PROF_TAG_NAME_STRING = "uaProfTagName";
field public static final java.lang.String KEY_MMS_UA_PROF_URL_STRING = "uaProfUrl";
field public static final java.lang.String KEY_MMS_USER_AGENT_STRING = "userAgent";
field public static final java.lang.String KEY_MONTHLY_DATA_CYCLE_DAY_INT = "monthly_data_cycle_day_int";
field public static final deprecated java.lang.String KEY_MONTHLY_DATA_CYCLE_DAY_INT = "monthly_data_cycle_day_int";
field public static final java.lang.String KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY = "only_single_dc_allowed_int_array";
field public static final java.lang.String KEY_OPERATOR_SELECTION_EXPAND_BOOL = "operator_selection_expand_bool";
field public static final java.lang.String KEY_PREFER_2G_BOOL = "prefer_2g_bool";
@@ -40149,8 +40150,10 @@ package android.telephony {
method public static int getDefaultSmsSubscriptionId();
method public static int getDefaultSubscriptionId();
method public static int getDefaultVoiceSubscriptionId();
method public java.util.List<android.telephony.SubscriptionPlan> getSubscriptionPlans(int);
method public boolean isNetworkRoaming(int);
method public void removeOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
method public void setSubscriptionPlans(int, java.util.List<android.telephony.SubscriptionPlan>);
field public static final java.lang.String ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED = "android.telephony.action.DEFAULT_SMS_SUBSCRIPTION_CHANGED";
field public static final java.lang.String ACTION_DEFAULT_SUBSCRIPTION_CHANGED = "android.telephony.action.DEFAULT_SUBSCRIPTION_CHANGED";
field public static final int DATA_ROAMING_DISABLE = 0; // 0x0
@@ -40164,6 +40167,38 @@ package android.telephony {
method public void onSubscriptionsChanged();
}
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 int describeContents();
method public int getDataLimitBehavior();
method public long getDataLimitBytes();
method public long getDataUsageBytes();
method public long getDataUsageTime();
method public java.lang.CharSequence getSummary();
method public java.lang.CharSequence getTitle();
method public void writeToParcel(android.os.Parcel, int);
field public static final long BYTES_UNKNOWN = -1L; // 0xffffffffffffffffL
field public static final long BYTES_UNLIMITED = 9223372036854775807L; // 0x7fffffffffffffffL
field public static final android.os.Parcelable.Creator<android.telephony.SubscriptionPlan> CREATOR;
field public static final int LIMIT_BEHAVIOR_BILLED = 1; // 0x1
field public static final int LIMIT_BEHAVIOR_DISABLED = 0; // 0x0
field public static final int LIMIT_BEHAVIOR_THROTTLED = 2; // 0x2
field public static final int LIMIT_BEHAVIOR_UNKNOWN = -1; // 0xffffffff
field public static final long TIME_UNKNOWN = -1L; // 0xffffffffffffffffL
}
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 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);
method public android.telephony.SubscriptionPlan.Builder setTitle(java.lang.CharSequence);
}
public class TelephonyManager {
method public boolean canChangeDtmfToneLength();
method public android.telephony.TelephonyManager createForPhoneAccountHandle(android.telecom.PhoneAccountHandle);

View File

@@ -146,7 +146,6 @@ package android {
field public static final java.lang.String MANAGE_CA_CERTIFICATES = "android.permission.MANAGE_CA_CERTIFICATES";
field public static final java.lang.String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
field public static final java.lang.String MANAGE_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS";
field public static final java.lang.String MANAGE_FALLBACK_SUBSCRIPTION_PLANS = "android.permission.MANAGE_FALLBACK_SUBSCRIPTION_PLANS";
field public static final java.lang.String MANAGE_OWN_CALLS = "android.permission.MANAGE_OWN_CALLS";
field public static final java.lang.String MANAGE_USB = "android.permission.MANAGE_USB";
field public static final java.lang.String MANAGE_USERS = "android.permission.MANAGE_USERS";
@@ -43083,7 +43082,7 @@ package android.telephony {
method public void notifyConfigChangedForSubId(int);
method public void updateConfigForPhoneId(int, java.lang.String);
field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
field public static final int DATA_CYCLE_THRESHOLD_DISABLED = -2; // 0xfffffffe
field public static final deprecated int DATA_CYCLE_THRESHOLD_DISABLED = -2; // 0xfffffffe
field public static final java.lang.String KEY_ADDITIONAL_CALL_SETTING_BOOL = "additional_call_setting_bool";
field public static final java.lang.String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool";
field public static final java.lang.String KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL = "allow_add_call_during_video_call";
@@ -43125,9 +43124,10 @@ package android.telephony {
field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING = "ci_action_on_sys_update_extra_val_string";
field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING = "ci_action_on_sys_update_intent_string";
field public static final java.lang.String KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING = "config_ims_package_override_string";
field public static final java.lang.String KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING = "config_plans_package_override_string";
field public static final java.lang.String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool";
field public static final java.lang.String KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG = "data_limit_threshold_bytes_long";
field public static final java.lang.String KEY_DATA_WARNING_THRESHOLD_BYTES_LONG = "data_warning_threshold_bytes_long";
field public static final deprecated java.lang.String KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG = "data_limit_threshold_bytes_long";
field public static final deprecated java.lang.String KEY_DATA_WARNING_THRESHOLD_BYTES_LONG = "data_warning_threshold_bytes_long";
field public static final java.lang.String KEY_DEFAULT_SIM_CALL_MANAGER_STRING = "default_sim_call_manager_string";
field public static final java.lang.String KEY_DEFAULT_VM_NUMBER_STRING = "default_vm_number_string";
field public static final java.lang.String KEY_DIAL_STRING_REPLACE_STRING_ARRAY = "dial_string_replace_string_array";
@@ -43184,7 +43184,7 @@ package android.telephony {
field public static final java.lang.String KEY_MMS_UA_PROF_TAG_NAME_STRING = "uaProfTagName";
field public static final java.lang.String KEY_MMS_UA_PROF_URL_STRING = "uaProfUrl";
field public static final java.lang.String KEY_MMS_USER_AGENT_STRING = "userAgent";
field public static final java.lang.String KEY_MONTHLY_DATA_CYCLE_DAY_INT = "monthly_data_cycle_day_int";
field public static final deprecated java.lang.String KEY_MONTHLY_DATA_CYCLE_DAY_INT = "monthly_data_cycle_day_int";
field public static final java.lang.String KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY = "only_single_dc_allowed_int_array";
field public static final java.lang.String KEY_OPERATOR_SELECTION_EXPAND_BOOL = "operator_selection_expand_bool";
field public static final java.lang.String KEY_PREFER_2G_BOOL = "prefer_2g_bool";
@@ -43698,8 +43698,10 @@ package android.telephony {
method public static int getDefaultSmsSubscriptionId();
method public static int getDefaultSubscriptionId();
method public static int getDefaultVoiceSubscriptionId();
method public java.util.List<android.telephony.SubscriptionPlan> getSubscriptionPlans(int);
method public boolean isNetworkRoaming(int);
method public void removeOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
method public void setSubscriptionPlans(int, java.util.List<android.telephony.SubscriptionPlan>);
field public static final java.lang.String ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED = "android.telephony.action.DEFAULT_SMS_SUBSCRIPTION_CHANGED";
field public static final java.lang.String ACTION_DEFAULT_SUBSCRIPTION_CHANGED = "android.telephony.action.DEFAULT_SUBSCRIPTION_CHANGED";
field public static final int DATA_ROAMING_DISABLE = 0; // 0x0
@@ -43713,6 +43715,38 @@ package android.telephony {
method public void onSubscriptionsChanged();
}
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 int describeContents();
method public int getDataLimitBehavior();
method public long getDataLimitBytes();
method public long getDataUsageBytes();
method public long getDataUsageTime();
method public java.lang.CharSequence getSummary();
method public java.lang.CharSequence getTitle();
method public void writeToParcel(android.os.Parcel, int);
field public static final long BYTES_UNKNOWN = -1L; // 0xffffffffffffffffL
field public static final long BYTES_UNLIMITED = 9223372036854775807L; // 0x7fffffffffffffffL
field public static final android.os.Parcelable.Creator<android.telephony.SubscriptionPlan> CREATOR;
field public static final int LIMIT_BEHAVIOR_BILLED = 1; // 0x1
field public static final int LIMIT_BEHAVIOR_DISABLED = 0; // 0x0
field public static final int LIMIT_BEHAVIOR_THROTTLED = 2; // 0x2
field public static final int LIMIT_BEHAVIOR_UNKNOWN = -1; // 0xffffffff
field public static final long TIME_UNKNOWN = -1L; // 0xffffffffffffffffL
}
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 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);
method public android.telephony.SubscriptionPlan.Builder setTitle(java.lang.CharSequence);
}
public final class TelephonyHistogram implements android.os.Parcelable {
ctor public TelephonyHistogram(int, int, int);
ctor public TelephonyHistogram(android.telephony.TelephonyHistogram);

View File

@@ -39762,7 +39762,7 @@ package android.telephony {
method public android.os.PersistableBundle getConfigForSubId(int);
method public void notifyConfigChangedForSubId(int);
field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
field public static final int DATA_CYCLE_THRESHOLD_DISABLED = -2; // 0xfffffffe
field public static final deprecated int DATA_CYCLE_THRESHOLD_DISABLED = -2; // 0xfffffffe
field public static final java.lang.String KEY_ADDITIONAL_CALL_SETTING_BOOL = "additional_call_setting_bool";
field public static final java.lang.String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool";
field public static final java.lang.String KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL = "allow_add_call_during_video_call";
@@ -39804,9 +39804,10 @@ package android.telephony {
field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING = "ci_action_on_sys_update_extra_val_string";
field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING = "ci_action_on_sys_update_intent_string";
field public static final java.lang.String KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING = "config_ims_package_override_string";
field public static final java.lang.String KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING = "config_plans_package_override_string";
field public static final java.lang.String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool";
field public static final java.lang.String KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG = "data_limit_threshold_bytes_long";
field public static final java.lang.String KEY_DATA_WARNING_THRESHOLD_BYTES_LONG = "data_warning_threshold_bytes_long";
field public static final deprecated java.lang.String KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG = "data_limit_threshold_bytes_long";
field public static final deprecated java.lang.String KEY_DATA_WARNING_THRESHOLD_BYTES_LONG = "data_warning_threshold_bytes_long";
field public static final java.lang.String KEY_DEFAULT_SIM_CALL_MANAGER_STRING = "default_sim_call_manager_string";
field public static final java.lang.String KEY_DEFAULT_VM_NUMBER_STRING = "default_vm_number_string";
field public static final java.lang.String KEY_DIAL_STRING_REPLACE_STRING_ARRAY = "dial_string_replace_string_array";
@@ -39863,7 +39864,7 @@ package android.telephony {
field public static final java.lang.String KEY_MMS_UA_PROF_TAG_NAME_STRING = "uaProfTagName";
field public static final java.lang.String KEY_MMS_UA_PROF_URL_STRING = "uaProfUrl";
field public static final java.lang.String KEY_MMS_USER_AGENT_STRING = "userAgent";
field public static final java.lang.String KEY_MONTHLY_DATA_CYCLE_DAY_INT = "monthly_data_cycle_day_int";
field public static final deprecated java.lang.String KEY_MONTHLY_DATA_CYCLE_DAY_INT = "monthly_data_cycle_day_int";
field public static final java.lang.String KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY = "only_single_dc_allowed_int_array";
field public static final java.lang.String KEY_OPERATOR_SELECTION_EXPAND_BOOL = "operator_selection_expand_bool";
field public static final java.lang.String KEY_PREFER_2G_BOOL = "prefer_2g_bool";
@@ -40375,8 +40376,10 @@ package android.telephony {
method public static int getDefaultSmsSubscriptionId();
method public static int getDefaultSubscriptionId();
method public static int getDefaultVoiceSubscriptionId();
method public java.util.List<android.telephony.SubscriptionPlan> getSubscriptionPlans(int);
method public boolean isNetworkRoaming(int);
method public void removeOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
method public void setSubscriptionPlans(int, java.util.List<android.telephony.SubscriptionPlan>);
field public static final java.lang.String ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED = "android.telephony.action.DEFAULT_SMS_SUBSCRIPTION_CHANGED";
field public static final java.lang.String ACTION_DEFAULT_SUBSCRIPTION_CHANGED = "android.telephony.action.DEFAULT_SUBSCRIPTION_CHANGED";
field public static final int DATA_ROAMING_DISABLE = 0; // 0x0
@@ -40390,6 +40393,38 @@ package android.telephony {
method public void onSubscriptionsChanged();
}
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 int describeContents();
method public int getDataLimitBehavior();
method public long getDataLimitBytes();
method public long getDataUsageBytes();
method public long getDataUsageTime();
method public java.lang.CharSequence getSummary();
method public java.lang.CharSequence getTitle();
method public void writeToParcel(android.os.Parcel, int);
field public static final long BYTES_UNKNOWN = -1L; // 0xffffffffffffffffL
field public static final long BYTES_UNLIMITED = 9223372036854775807L; // 0x7fffffffffffffffL
field public static final android.os.Parcelable.Creator<android.telephony.SubscriptionPlan> CREATOR;
field public static final int LIMIT_BEHAVIOR_BILLED = 1; // 0x1
field public static final int LIMIT_BEHAVIOR_DISABLED = 0; // 0x0
field public static final int LIMIT_BEHAVIOR_THROTTLED = 2; // 0x2
field public static final int LIMIT_BEHAVIOR_UNKNOWN = -1; // 0xffffffff
field public static final long TIME_UNKNOWN = -1L; // 0xffffffffffffffffL
}
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 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);
method public android.telephony.SubscriptionPlan.Builder setTitle(java.lang.CharSequence);
}
public class TelephonyManager {
method public boolean canChangeDtmfToneLength();
method public android.telephony.TelephonyManager createForPhoneAccountHandle(android.telecom.PhoneAccountHandle);

View File

@@ -71,7 +71,5 @@ interface INetworkPolicyManager {
SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage);
void setSubscriptionPlans(int subId, in SubscriptionPlan[] plans, String callingPackage);
String getSubscriptionPlanOwner(int subId);
void factoryReset(String subscriber);
}

View File

@@ -16,16 +16,21 @@
package android.net;
import static com.android.internal.util.Preconditions.checkNotNull;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.BackupUtils;
import android.util.Pair;
import android.util.RecurrenceRule;
import com.android.internal.util.Preconditions;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Iterator;
import java.util.Objects;
/**
@@ -35,10 +40,8 @@ import java.util.Objects;
* @hide
*/
public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> {
/**
* Current Version of the Backup Serializer.
*/
private static final int BACKUP_VERSION = 1;
private static final int VERSION_INIT = 1;
private static final int VERSION_RULE = 2;
public static final int CYCLE_NONE = -1;
public static final long WARNING_DISABLED = -1;
@@ -46,8 +49,7 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> {
public static final long SNOOZE_NEVER = -1;
public NetworkTemplate template;
@Deprecated public int cycleDay = CYCLE_NONE;
@Deprecated public String cycleTimezone = "UTC";
public RecurrenceRule cycleRule;
public long warningBytes = WARNING_DISABLED;
public long limitBytes = LIMIT_DISABLED;
public long lastWarningSnooze = SNOOZE_NEVER;
@@ -57,7 +59,12 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> {
private static final long DEFAULT_MTU = 1500;
public NetworkPolicy() {
public static RecurrenceRule buildRule(int cycleDay, ZoneId cycleTimezone) {
if (cycleDay != NetworkPolicy.CYCLE_NONE) {
return RecurrenceRule.buildRecurringMonthly(cycleDay, cycleTimezone);
} else {
return RecurrenceRule.buildNever();
}
}
@Deprecated
@@ -67,12 +74,19 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> {
SNOOZE_NEVER, metered, false);
}
@Deprecated
public NetworkPolicy(NetworkTemplate template, int cycleDay, String cycleTimezone,
long warningBytes, long limitBytes, long lastWarningSnooze, long lastLimitSnooze,
boolean metered, boolean inferred) {
this.template = checkNotNull(template, "missing NetworkTemplate");
this.cycleDay = cycleDay;
this.cycleTimezone = checkNotNull(cycleTimezone, "missing cycleTimezone");
this(template, buildRule(cycleDay, ZoneId.of(cycleTimezone)), warningBytes,
limitBytes, lastWarningSnooze, lastLimitSnooze, metered, inferred);
}
public NetworkPolicy(NetworkTemplate template, RecurrenceRule cycleRule, long warningBytes,
long limitBytes, long lastWarningSnooze, long lastLimitSnooze, boolean metered,
boolean inferred) {
this.template = Preconditions.checkNotNull(template, "missing NetworkTemplate");
this.cycleRule = Preconditions.checkNotNull(cycleRule, "missing RecurrenceRule");
this.warningBytes = warningBytes;
this.limitBytes = limitBytes;
this.lastWarningSnooze = lastWarningSnooze;
@@ -81,23 +95,21 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> {
this.inferred = inferred;
}
public NetworkPolicy(Parcel in) {
template = in.readParcelable(null);
cycleDay = in.readInt();
cycleTimezone = in.readString();
warningBytes = in.readLong();
limitBytes = in.readLong();
lastWarningSnooze = in.readLong();
lastLimitSnooze = in.readLong();
metered = in.readInt() != 0;
inferred = in.readInt() != 0;
private NetworkPolicy(Parcel source) {
template = source.readParcelable(null);
cycleRule = source.readParcelable(null);
warningBytes = source.readLong();
limitBytes = source.readLong();
lastWarningSnooze = source.readLong();
lastLimitSnooze = source.readLong();
metered = source.readInt() != 0;
inferred = source.readInt() != 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(template, flags);
dest.writeInt(cycleDay);
dest.writeString(cycleTimezone);
dest.writeParcelable(cycleRule, flags);
dest.writeLong(warningBytes);
dest.writeLong(limitBytes);
dest.writeLong(lastWarningSnooze);
@@ -111,6 +123,10 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> {
return 0;
}
public Iterator<Pair<ZonedDateTime, ZonedDateTime>> cycleIterator() {
return cycleRule.cycleIterator();
}
/**
* Test if given measurement is over {@link #warningBytes}.
*/
@@ -141,7 +157,7 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> {
* Test if this policy has a cycle defined, after which usage should reset.
*/
public boolean hasCycle() {
return cycleDay != CYCLE_NONE;
return cycleRule.cycleIterator().hasNext();
}
@Override
@@ -159,7 +175,7 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> {
@Override
public int hashCode() {
return Objects.hash(template, cycleDay, cycleTimezone, warningBytes, limitBytes,
return Objects.hash(template, cycleRule, warningBytes, limitBytes,
lastWarningSnooze, lastLimitSnooze, metered, inferred);
}
@@ -167,30 +183,29 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> {
public boolean equals(Object obj) {
if (obj instanceof NetworkPolicy) {
final NetworkPolicy other = (NetworkPolicy) obj;
return cycleDay == other.cycleDay && warningBytes == other.warningBytes
return warningBytes == other.warningBytes
&& limitBytes == other.limitBytes
&& lastWarningSnooze == other.lastWarningSnooze
&& lastLimitSnooze == other.lastLimitSnooze && metered == other.metered
&& inferred == other.inferred
&& Objects.equals(cycleTimezone, other.cycleTimezone)
&& Objects.equals(template, other.template);
&& Objects.equals(template, other.template)
&& Objects.equals(cycleRule, other.cycleRule);
}
return false;
}
@Override
public String toString() {
final StringBuilder builder = new StringBuilder("NetworkPolicy");
builder.append("[").append(template).append("]:");
builder.append(" cycleDay=").append(cycleDay);
builder.append(", cycleTimezone=").append(cycleTimezone);
builder.append(", warningBytes=").append(warningBytes);
builder.append(", limitBytes=").append(limitBytes);
builder.append(", lastWarningSnooze=").append(lastWarningSnooze);
builder.append(", lastLimitSnooze=").append(lastLimitSnooze);
builder.append(", metered=").append(metered);
builder.append(", inferred=").append(inferred);
return builder.toString();
return new StringBuilder("NetworkPolicy{")
.append("template=").append(template)
.append(" cycleRule=").append(cycleRule)
.append(" warningBytes=").append(warningBytes)
.append(" limitBytes=").append(limitBytes)
.append(" lastWarningSnooze=").append(lastWarningSnooze)
.append(" lastLimitSnooze=").append(lastLimitSnooze)
.append(" metered=").append(metered)
.append(" inferred=").append(inferred)
.append("}").toString();
}
public static final Creator<NetworkPolicy> CREATOR = new Creator<NetworkPolicy>() {
@@ -209,10 +224,9 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(baos);
out.writeInt(BACKUP_VERSION);
out.writeInt(VERSION_RULE);
out.write(template.getBytesForBackup());
out.writeInt(cycleDay);
BackupUtils.writeString(out, cycleTimezone);
cycleRule.writeToStream(out);
out.writeLong(warningBytes);
out.writeLong(limitBytes);
out.writeLong(lastWarningSnooze);
@@ -224,21 +238,36 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> {
public static NetworkPolicy getNetworkPolicyFromBackup(DataInputStream in) throws IOException,
BackupUtils.BadVersionException {
int version = in.readInt();
if (version < 1 || version > BACKUP_VERSION) {
throw new BackupUtils.BadVersionException("Unknown Backup Serialization Version");
final int version = in.readInt();
switch (version) {
case VERSION_INIT: {
NetworkTemplate template = NetworkTemplate.getNetworkTemplateFromBackup(in);
int cycleDay = in.readInt();
String cycleTimeZone = BackupUtils.readString(in);
long warningBytes = in.readLong();
long limitBytes = in.readLong();
long lastWarningSnooze = in.readLong();
long lastLimitSnooze = in.readLong();
boolean metered = in.readInt() == 1;
boolean inferred = in.readInt() == 1;
return new NetworkPolicy(template, cycleDay, cycleTimeZone, warningBytes,
limitBytes, lastWarningSnooze, lastLimitSnooze, metered, inferred);
}
case VERSION_RULE: {
NetworkTemplate template = NetworkTemplate.getNetworkTemplateFromBackup(in);
RecurrenceRule cycleRule = new RecurrenceRule(in);
long warningBytes = in.readLong();
long limitBytes = in.readLong();
long lastWarningSnooze = in.readLong();
long lastLimitSnooze = in.readLong();
boolean metered = in.readInt() == 1;
boolean inferred = in.readInt() == 1;
return new NetworkPolicy(template, cycleRule, warningBytes,
limitBytes, lastWarningSnooze, lastLimitSnooze, metered, inferred);
}
default: {
throw new BackupUtils.BadVersionException("Unknown backup version: " + version);
}
}
NetworkTemplate template = NetworkTemplate.getNetworkTemplateFromBackup(in);
int cycleDay = in.readInt();
String cycleTimeZone = BackupUtils.readString(in);
long warningBytes = in.readLong();
long limitBytes = in.readLong();
long lastWarningSnooze = in.readLong();
long lastLimitSnooze = in.readLong();
boolean metered = in.readInt() == 1;
boolean inferred = in.readInt() == 1;
return new NetworkPolicy(template, cycleDay, cycleTimeZone, warningBytes, limitBytes,
lastWarningSnooze, lastLimitSnooze, metered, inferred);
}
}

View File

@@ -254,7 +254,7 @@ public class NetworkPolicyManager {
/** {@hide} */
public static Iterator<Pair<ZonedDateTime, ZonedDateTime>> cycleIterator(NetworkPolicy policy) {
return SubscriptionPlan.convert(policy).cycleIterator();
return policy.cycleIterator();
}
/**

View File

@@ -0,0 +1,257 @@
/*
* 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.util;
import android.os.Parcel;
import android.os.Parcelable;
import com.android.internal.annotations.VisibleForTesting;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ProtocolException;
import java.time.Clock;
import java.time.LocalTime;
import java.time.Period;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Iterator;
import java.util.Objects;
/**
* Description of an event that should recur over time at a specific interval
* between two anchor points in time.
*
* @hide
*/
public class RecurrenceRule implements Parcelable {
private static final String TAG = "RecurrenceRule";
private static final boolean DEBUG = true;
private static final int VERSION_INIT = 0;
/** {@hide} */
@VisibleForTesting
public static Clock sClock = Clock.systemDefaultZone();
public final ZonedDateTime start;
public final ZonedDateTime end;
public final Period period;
public RecurrenceRule(ZonedDateTime start, ZonedDateTime end, Period period) {
this.start = start;
this.end = end;
this.period = period;
}
@Deprecated
public static RecurrenceRule buildNever() {
return new RecurrenceRule(null, null, null);
}
@Deprecated
public static RecurrenceRule buildRecurringMonthly(int dayOfMonth, ZoneId zone) {
// Assume we started last January, since it has all possible days
final ZonedDateTime now = ZonedDateTime.now(sClock).withZoneSameInstant(zone);
final ZonedDateTime start = ZonedDateTime.of(
now.toLocalDate().minusYears(1).withMonth(1).withDayOfMonth(dayOfMonth),
LocalTime.MIDNIGHT, zone);
return new RecurrenceRule(start, null, Period.ofMonths(1));
}
private RecurrenceRule(Parcel source) {
start = convertZonedDateTime(source.readString());
end = convertZonedDateTime(source.readString());
period = convertPeriod(source.readString());
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(convertZonedDateTime(start));
dest.writeString(convertZonedDateTime(end));
dest.writeString(convertPeriod(period));
}
public RecurrenceRule(DataInputStream in) throws IOException {
final int version = in.readInt();
switch (version) {
case VERSION_INIT:
start = convertZonedDateTime(BackupUtils.readString(in));
end = convertZonedDateTime(BackupUtils.readString(in));
period = convertPeriod(BackupUtils.readString(in));
default:
throw new ProtocolException("Unknown version " + version);
}
}
public void writeToStream(DataOutputStream out) throws IOException {
out.writeInt(VERSION_INIT);
BackupUtils.writeString(out, convertZonedDateTime(start));
BackupUtils.writeString(out, convertZonedDateTime(end));
BackupUtils.writeString(out, convertPeriod(period));
}
@Override
public String toString() {
return new StringBuilder("RecurrenceRule{")
.append("start=").append(start)
.append(" end=").append(end)
.append(" period=").append(period)
.append("}").toString();
}
@Override
public int hashCode() {
return Objects.hash(start, end, period);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof RecurrenceRule) {
final RecurrenceRule other = (RecurrenceRule) obj;
return Objects.equals(start, other.start)
&& Objects.equals(end, other.end)
&& Objects.equals(period, other.period);
}
return false;
}
public static final Parcelable.Creator<RecurrenceRule> CREATOR = new Parcelable.Creator<RecurrenceRule>() {
@Override
public RecurrenceRule createFromParcel(Parcel source) {
return new RecurrenceRule(source);
}
@Override
public RecurrenceRule[] newArray(int size) {
return new RecurrenceRule[size];
}
};
@Deprecated
public boolean isMonthly() {
return start != null
&& period != null
&& period.getYears() == 0
&& period.getMonths() == 1
&& period.getDays() == 0;
}
public Iterator<Pair<ZonedDateTime, ZonedDateTime>> cycleIterator() {
if (period != null) {
return new RecurringIterator();
} else {
return new NonrecurringIterator();
}
}
private class NonrecurringIterator implements Iterator<Pair<ZonedDateTime, ZonedDateTime>> {
boolean hasNext;
public NonrecurringIterator() {
hasNext = (start != null) && (end != null);
}
@Override
public boolean hasNext() {
return hasNext;
}
@Override
public Pair<ZonedDateTime, ZonedDateTime> next() {
hasNext = false;
return new Pair<>(start, end);
}
}
private class RecurringIterator implements Iterator<Pair<ZonedDateTime, ZonedDateTime>> {
int i;
ZonedDateTime cycleStart;
ZonedDateTime cycleEnd;
public RecurringIterator() {
final ZonedDateTime anchor = (end != null) ? end
: ZonedDateTime.now(sClock).withZoneSameInstant(start.getZone());
if (DEBUG) Log.d(TAG, "Resolving using anchor " + anchor);
updateCycle();
// Walk forwards until we find first cycle after now
while (anchor.toEpochSecond() > cycleEnd.toEpochSecond()) {
i++;
updateCycle();
}
// Walk backwards until we find first cycle before now
while (anchor.toEpochSecond() <= cycleStart.toEpochSecond()) {
i--;
updateCycle();
}
}
private void updateCycle() {
cycleStart = roundBoundaryTime(start.plus(period.multipliedBy(i)));
cycleEnd = roundBoundaryTime(start.plus(period.multipliedBy(i + 1)));
}
private ZonedDateTime roundBoundaryTime(ZonedDateTime boundary) {
if (isMonthly() && (boundary.getDayOfMonth() < start.getDayOfMonth())) {
// When forced to end a monthly cycle early, we want to count
// that entire day against the boundary.
return ZonedDateTime.of(boundary.toLocalDate(), LocalTime.MAX, start.getZone());
} else {
return boundary;
}
}
@Override
public boolean hasNext() {
return cycleStart.toEpochSecond() >= start.toEpochSecond();
}
@Override
public Pair<ZonedDateTime, ZonedDateTime> next() {
if (DEBUG) Log.d(TAG, "Cycle " + i + " from " + cycleStart + " to " + cycleEnd);
Pair<ZonedDateTime, ZonedDateTime> p = new Pair<>(cycleStart, cycleEnd);
i--;
updateCycle();
return p;
}
}
public static String convertZonedDateTime(ZonedDateTime time) {
return time != null ? time.toString() : null;
}
public static ZonedDateTime convertZonedDateTime(String time) {
return time != null ? ZonedDateTime.parse(time) : null;
}
public static String convertPeriod(Period period) {
return period != null ? period.toString() : null;
}
public static Period convertPeriod(String period) {
return period != null ? Period.parse(period) : null;
}
}

View File

@@ -1694,10 +1694,10 @@ public class XmlUtils {
return in.getAttributeValue(null, name);
}
public static void writeStringAttribute(XmlSerializer out, String name, String value)
public static void writeStringAttribute(XmlSerializer out, String name, CharSequence value)
throws IOException {
if (value != null) {
out.attribute(null, name, value);
out.attribute(null, name, value.toString());
}
}

View File

@@ -3156,12 +3156,6 @@
<permission android:name="android.permission.MANAGE_NETWORK_POLICY"
android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to manage fallback subscription plans.
Note that another app providing plans for an explicit HNI will always
take precidence over these fallback plans. @hide -->
<permission android:name="android.permission.MANAGE_FALLBACK_SUBSCRIPTION_PLANS"
android:protectionLevel="signature|privileged" />
<!-- @SystemApi @hide @deprecated use UPDATE_DEVICE_STATS instead -->
<permission android:name="android.permission.MODIFY_NETWORK_ACCOUNTING"
android:protectionLevel="signature|privileged" />

View File

@@ -0,0 +1,146 @@
/*
* 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.util;
import android.support.test.filters.SmallTest;
import junit.framework.TestCase;
import java.time.Clock;
import java.time.Instant;
import java.time.Period;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Iterator;
@SmallTest
public class RecurrenceRuleTest extends TestCase {
static Clock sOriginalClock;
@Override
protected void setUp() throws Exception {
super.setUp();
sOriginalClock = RecurrenceRule.sClock;
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
RecurrenceRule.sClock = sOriginalClock;
}
private void setClock(Instant instant) {
RecurrenceRule.sClock = Clock.fixed(instant, ZoneId.systemDefault());
}
public void testSimpleMonth() throws Exception {
setClock(Instant.parse("2015-11-20T10:15:30.00Z"));
final RecurrenceRule r = new RecurrenceRule(
ZonedDateTime.parse("2010-11-14T00:00:00.000Z"),
null,
Period.ofMonths(1));
assertTrue(r.isMonthly());
final Iterator<Pair<ZonedDateTime, ZonedDateTime>> it = r.cycleIterator();
assertTrue(it.hasNext());
assertEquals(Pair.create(
ZonedDateTime.parse("2015-11-14T00:00:00.00Z"),
ZonedDateTime.parse("2015-12-14T00:00:00.00Z")), it.next());
assertTrue(it.hasNext());
assertEquals(Pair.create(
ZonedDateTime.parse("2015-10-14T00:00:00.00Z"),
ZonedDateTime.parse("2015-11-14T00:00:00.00Z")), it.next());
}
public void testSimpleDays() throws Exception {
setClock(Instant.parse("2015-01-01T10:15:30.00Z"));
final RecurrenceRule r = new RecurrenceRule(
ZonedDateTime.parse("2010-11-14T00:11:00.000Z"),
ZonedDateTime.parse("2010-11-20T00:11:00.000Z"),
Period.ofDays(3));
assertFalse(r.isMonthly());
final Iterator<Pair<ZonedDateTime, ZonedDateTime>> it = r.cycleIterator();
assertTrue(it.hasNext());
assertEquals(Pair.create(
ZonedDateTime.parse("2010-11-17T00:11:00.00Z"),
ZonedDateTime.parse("2010-11-20T00:11:00.00Z")), it.next());
assertTrue(it.hasNext());
assertEquals(Pair.create(
ZonedDateTime.parse("2010-11-14T00:11:00.00Z"),
ZonedDateTime.parse("2010-11-17T00:11:00.00Z")), it.next());
assertFalse(it.hasNext());
}
public void testNotRecurring() throws Exception {
setClock(Instant.parse("2015-01-01T10:15:30.00Z"));
final RecurrenceRule r = new RecurrenceRule(
ZonedDateTime.parse("2010-11-14T00:11:00.000Z"),
ZonedDateTime.parse("2010-11-20T00:11:00.000Z"),
null);
assertFalse(r.isMonthly());
final Iterator<Pair<ZonedDateTime, ZonedDateTime>> it = r.cycleIterator();
assertTrue(it.hasNext());
assertEquals(Pair.create(
ZonedDateTime.parse("2010-11-14T00:11:00.000Z"),
ZonedDateTime.parse("2010-11-20T00:11:00.000Z")), it.next());
assertFalse(it.hasNext());
}
public void testNever() throws Exception {
setClock(Instant.parse("2015-01-01T10:15:30.00Z"));
final RecurrenceRule r = RecurrenceRule.buildNever();
assertFalse(r.isMonthly());
final Iterator<Pair<ZonedDateTime, ZonedDateTime>> it = r.cycleIterator();
assertFalse(it.hasNext());
}
public void testSane() throws Exception {
final RecurrenceRule r = new RecurrenceRule(
ZonedDateTime.parse("1980-01-31T00:00:00.000Z"),
ZonedDateTime.parse("2030-01-31T00:00:00.000Z"),
Period.ofMonths(1));
final Iterator<Pair<ZonedDateTime, ZonedDateTime>> it = r.cycleIterator();
ZonedDateTime lastStart = null;
int months = 0;
while (it.hasNext()) {
final Pair<ZonedDateTime, ZonedDateTime> cycle = it.next();
// Make sure cycle has reasonable length
final long length = cycle.second.toEpochSecond() - cycle.first.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);
}
lastStart = cycle.first;
months++;
}
assertEquals(600, months);
}
}

View File

@@ -30,10 +30,12 @@ import android.net.NetworkTemplate;
import android.net.wifi.WifiInfo;
import android.os.AsyncTask;
import android.text.TextUtils;
import android.text.format.Time;
import android.util.RecurrenceRule;
import com.google.android.collect.Lists;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
/**
@@ -129,35 +131,36 @@ public class NetworkPolicyEditor {
@Deprecated
private static NetworkPolicy buildDefaultPolicy(NetworkTemplate template) {
// TODO: move this into framework to share with NetworkPolicyManagerService
final int cycleDay;
final String cycleTimezone;
final RecurrenceRule cycleRule;
final boolean metered;
if (template.getMatchRule() == MATCH_WIFI) {
cycleDay = CYCLE_NONE;
cycleTimezone = Time.TIMEZONE_UTC;
cycleRule = RecurrenceRule.buildNever();
metered = false;
} else {
final Time time = new Time();
time.setToNow();
cycleDay = time.monthDay;
cycleTimezone = time.timezone;
cycleRule = RecurrenceRule.buildRecurringMonthly(ZonedDateTime.now().getDayOfMonth(),
ZoneId.systemDefault());
metered = true;
}
return new NetworkPolicy(template, cycleDay, cycleTimezone, WARNING_DISABLED,
return new NetworkPolicy(template, cycleRule, WARNING_DISABLED,
LIMIT_DISABLED, SNOOZE_NEVER, SNOOZE_NEVER, metered, true);
}
@Deprecated
public int getPolicyCycleDay(NetworkTemplate template) {
final NetworkPolicy policy = getPolicy(template);
return (policy != null) ? policy.cycleDay : CYCLE_NONE;
if (policy != null && policy.cycleRule.isMonthly()) {
return policy.cycleRule.start.getDayOfMonth();
} else {
return CYCLE_NONE;
}
}
@Deprecated
public void setPolicyCycleDay(NetworkTemplate template, int cycleDay, String cycleTimezone) {
final NetworkPolicy policy = getOrCreatePolicy(template);
policy.cycleDay = cycleDay;
policy.cycleTimezone = cycleTimezone;
policy.cycleRule = NetworkPolicy.buildRule(cycleDay, ZoneId.of(cycleTimezone));
policy.inferred = false;
policy.clearSnooze();
writeAsync();

View File

@@ -18,7 +18,6 @@ package com.android.server.net;
import static android.Manifest.permission.ACCESS_NETWORK_STATE;
import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
import static android.Manifest.permission.MANAGE_FALLBACK_SUBSCRIPTION_PLANS;
import static android.Manifest.permission.MANAGE_NETWORK_POLICY;
import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
import static android.Manifest.permission.READ_PHONE_STATE;
@@ -28,7 +27,6 @@ import static android.content.Intent.ACTION_UID_REMOVED;
import static android.content.Intent.ACTION_USER_ADDED;
import static android.content.Intent.ACTION_USER_REMOVED;
import static android.content.Intent.EXTRA_UID;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
@@ -76,9 +74,11 @@ import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.internal.util.XmlUtils.readBooleanAttribute;
import static com.android.internal.util.XmlUtils.readIntAttribute;
import static com.android.internal.util.XmlUtils.readLongAttribute;
import static com.android.internal.util.XmlUtils.readStringAttribute;
import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
import static com.android.internal.util.XmlUtils.writeIntAttribute;
import static com.android.internal.util.XmlUtils.writeLongAttribute;
import static com.android.internal.util.XmlUtils.writeStringAttribute;
import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_UPDATED;
@@ -157,14 +157,15 @@ import android.telephony.SubscriptionPlan;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.text.format.Formatter;
import android.text.format.Time;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Log;
import android.util.NtpTrustedTime;
import android.util.Pair;
import android.util.RecurrenceRule;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.TrustedTime;
@@ -180,6 +181,7 @@ import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.server.DeviceIdleController;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
@@ -192,7 +194,6 @@ import libcore.io.IoUtils;
import com.google.android.collect.Lists;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
import java.io.File;
@@ -205,6 +206,7 @@ import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.charset.StandardCharsets;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Arrays;
@@ -257,7 +259,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private static final int VERSION_SWITCH_APP_ID = 8;
private static final int VERSION_ADDED_NETWORK_ID = 9;
private static final int VERSION_SWITCH_UID = 10;
private static final int VERSION_LATEST = VERSION_SWITCH_UID;
private static final int VERSION_ADDED_CYCLE = 11;
private static final int VERSION_LATEST = VERSION_ADDED_CYCLE;
/**
* Max items written to {@link #ProcStateSeqHistory}.
@@ -275,6 +278,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private static final String TAG_POLICY_LIST = "policy-list";
private static final String TAG_NETWORK_POLICY = "network-policy";
private static final String TAG_SUBSCRIPTION_PLAN = "subscription-plan";
private static final String TAG_UID_POLICY = "uid-policy";
private static final String TAG_APP_POLICY = "app-policy";
private static final String TAG_WHITELIST = "whitelist";
@@ -286,8 +290,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private static final String ATTR_NETWORK_TEMPLATE = "networkTemplate";
private static final String ATTR_SUBSCRIBER_ID = "subscriberId";
private static final String ATTR_NETWORK_ID = "networkId";
private static final String ATTR_CYCLE_DAY = "cycleDay";
private static final String ATTR_CYCLE_TIMEZONE = "cycleTimezone";
@Deprecated private static final String ATTR_CYCLE_DAY = "cycleDay";
@Deprecated private static final String ATTR_CYCLE_TIMEZONE = "cycleTimezone";
private static final String ATTR_CYCLE_START = "cycleStart";
private static final String ATTR_CYCLE_END = "cycleEnd";
private static final String ATTR_CYCLE_PERIOD = "cyclePeriod";
private static final String ATTR_WARNING_BYTES = "warningBytes";
private static final String ATTR_LIMIT_BYTES = "limitBytes";
private static final String ATTR_LAST_SNOOZE = "lastSnooze";
@@ -298,6 +305,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private static final String ATTR_UID = "uid";
private static final String ATTR_APP_ID = "appId";
private static final String ATTR_POLICY = "policy";
private static final String ATTR_SUB_ID = "subId";
private static final String ATTR_TITLE = "title";
private static final String ATTR_SUMMARY = "summary";
private static final String ATTR_LIMIT_BEHAVIOR = "limitBehavior";
private static final String ATTR_USAGE_BYTES = "usageBytes";
private static final String ATTR_USAGE_TIME = "usageTime";
private static final String ACTION_ALLOW_BACKGROUND =
"com.android.server.net.action.ALLOW_BACKGROUND";
@@ -359,6 +372,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
/** Currently active network rules for ifaces. */
final ArrayMap<NetworkPolicy, String[]> mNetworkRules = new ArrayMap<>();
/** Defined subscription plans. */
final SparseArray<SubscriptionPlan[]> mSubscriptionPlans = new SparseArray<>();
/** Defined UID policies. */
@GuardedBy("mUidRulesFirstLock") final SparseIntArray mUidPolicy = new SparseIntArray();
/** Currently derived rules for each UID. */
@@ -998,7 +1014,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
// cycle boundary to recompute notifications.
// examine stats for each active policy
final long currentTime = currentTimeMillis();
for (int i = mNetworkPolicy.size()-1; i >= 0; i--) {
final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
// ignore policies that aren't relevant to user
@@ -1273,20 +1288,27 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
continue;
}
final int cycleDay = getCycleDayFromCarrierConfig(config, policy.cycleDay);
final int currentCycleDay;
if (policy.cycleRule.isMonthly()) {
currentCycleDay = policy.cycleRule.start.getDayOfMonth();
} else {
currentCycleDay = NetworkPolicy.CYCLE_NONE;
}
final int cycleDay = getCycleDayFromCarrierConfig(config, currentCycleDay);
final long warningBytes = getWarningBytesFromCarrierConfig(config,
policy.warningBytes);
final long limitBytes = getLimitBytesFromCarrierConfig(config,
policy.limitBytes);
if (policy.cycleDay == cycleDay &&
if (currentCycleDay == cycleDay &&
policy.warningBytes == warningBytes &&
policy.limitBytes == limitBytes) {
continue;
}
policyUpdated = true;
policy.cycleDay = cycleDay;
policy.cycleRule = NetworkPolicy.buildRule(cycleDay, ZoneId.systemDefault());
policy.warningBytes = warningBytes;
policy.limitBytes = limitBytes;
@@ -1456,7 +1478,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
// TODO: reset any policy-disabled networks when any policy is removed
// completely, which is currently rare case.
final long currentTime = currentTimeMillis();
for (int i = mNetworkPolicy.size()-1; i >= 0; i--) {
final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
// shortcut when policy has no limit
@@ -1573,7 +1594,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
// apply each policy that we found ifaces for; compute remaining data
// based on current cycle and historical stats, and push to kernel.
final long currentTime = currentTimeMillis();
for (int i = mNetworkRules.size()-1; i >= 0; i--) {
final NetworkPolicy policy = mNetworkRules.keyAt(i);
final String[] ifaces = mNetworkRules.valueAt(i);
@@ -1721,20 +1741,16 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
public NetworkPolicy buildDefaultMobilePolicy(int subId, String subscriberId) {
PersistableBundle config = mCarrierConfigManager.getConfigForSubId(subId);
// assume usage cycle starts today
final Time time = new Time();
time.setToNow();
final String cycleTimezone = time.timezone;
final int cycleDay = getCycleDayFromCarrierConfig(config, time.monthDay);
final int cycleDay = getCycleDayFromCarrierConfig(config,
ZonedDateTime.now().getDayOfMonth());
final long warningBytes = getWarningBytesFromCarrierConfig(config,
getPlatformDefaultWarningBytes());
final long limitBytes = getLimitBytesFromCarrierConfig(config,
getPlatformDefaultLimitBytes());
final NetworkTemplate template = buildTemplateMobileAll(subscriberId);
final NetworkPolicy policy = new NetworkPolicy(template, cycleDay, cycleTimezone,
final RecurrenceRule cycleRule = NetworkPolicy.buildRule(cycleDay, ZoneId.systemDefault());
final NetworkPolicy policy = new NetworkPolicy(template, cycleRule,
warningBytes, limitBytes, SNOOZE_NEVER, SNOOZE_NEVER, true, true);
return policy;
}
@@ -1744,6 +1760,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
// clear any existing policy and read from disk
mNetworkPolicy.clear();
mSubscriptionPlans.clear();
mUidPolicy.clear();
FileInputStream fis = null;
@@ -1787,12 +1804,24 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
} else {
networkId = null;
}
final int cycleDay = readIntAttribute(in, ATTR_CYCLE_DAY);
final String cycleTimezone;
if (version >= VERSION_ADDED_TIMEZONE) {
cycleTimezone = in.getAttributeValue(null, ATTR_CYCLE_TIMEZONE);
final RecurrenceRule cycleRule;
if (version >= VERSION_ADDED_CYCLE) {
final String start = readStringAttribute(in, ATTR_CYCLE_START);
final String end = readStringAttribute(in, ATTR_CYCLE_END);
final String period = readStringAttribute(in, ATTR_CYCLE_PERIOD);
cycleRule = new RecurrenceRule(
RecurrenceRule.convertZonedDateTime(start),
RecurrenceRule.convertZonedDateTime(end),
RecurrenceRule.convertPeriod(period));
} else {
cycleTimezone = Time.TIMEZONE_UTC;
final int cycleDay = readIntAttribute(in, ATTR_CYCLE_DAY);
final String cycleTimezone;
if (version >= VERSION_ADDED_TIMEZONE) {
cycleTimezone = in.getAttributeValue(null, ATTR_CYCLE_TIMEZONE);
} else {
cycleTimezone = "UTC";
}
cycleRule = NetworkPolicy.buildRule(cycleDay, ZoneId.of(cycleTimezone));
}
final long warningBytes = readLongAttribute(in, ATTR_WARNING_BYTES);
final long limitBytes = readLongAttribute(in, ATTR_LIMIT_BYTES);
@@ -1834,10 +1863,45 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final NetworkTemplate template = new NetworkTemplate(networkTemplate,
subscriberId, networkId);
if (template.isPersistable()) {
mNetworkPolicy.put(template, new NetworkPolicy(template, cycleDay,
cycleTimezone, warningBytes, limitBytes, lastWarningSnooze,
mNetworkPolicy.put(template, new NetworkPolicy(template, cycleRule,
warningBytes, limitBytes, lastWarningSnooze,
lastLimitSnooze, metered, inferred));
}
} else if (TAG_SUBSCRIPTION_PLAN.equals(tag)) {
final String start = readStringAttribute(in, ATTR_CYCLE_START);
final String end = readStringAttribute(in, ATTR_CYCLE_END);
final String period = readStringAttribute(in, ATTR_CYCLE_PERIOD);
final SubscriptionPlan.Builder builder = new SubscriptionPlan.Builder(
RecurrenceRule.convertZonedDateTime(start),
RecurrenceRule.convertZonedDateTime(end),
RecurrenceRule.convertPeriod(period));
builder.setTitle(readStringAttribute(in, ATTR_TITLE));
builder.setSummary(readStringAttribute(in, ATTR_SUMMARY));
final long limitBytes = readLongAttribute(in, ATTR_LIMIT_BYTES,
SubscriptionPlan.BYTES_UNKNOWN);
final int limitBehavior = readIntAttribute(in, ATTR_LIMIT_BEHAVIOR,
SubscriptionPlan.LIMIT_BEHAVIOR_UNKNOWN);
if (limitBytes != SubscriptionPlan.BYTES_UNKNOWN
&& limitBehavior != SubscriptionPlan.LIMIT_BEHAVIOR_UNKNOWN) {
builder.setDataLimit(limitBytes, limitBehavior);
}
final long usageBytes = readLongAttribute(in, ATTR_USAGE_BYTES,
SubscriptionPlan.BYTES_UNKNOWN);
final long usageTime = readLongAttribute(in, ATTR_USAGE_TIME,
SubscriptionPlan.TIME_UNKNOWN);
if (usageBytes != SubscriptionPlan.BYTES_UNKNOWN
&& usageTime != SubscriptionPlan.TIME_UNKNOWN) {
builder.setDataUsage(usageBytes, usageTime);
}
final int subId = readIntAttribute(in, ATTR_SUB_ID);
final SubscriptionPlan plan = builder.build();
mSubscriptionPlans.put(subId, ArrayUtils.appendElement(
SubscriptionPlan.class, mSubscriptionPlans.get(subId), plan));
} else if (TAG_UID_POLICY.equals(tag)) {
final int uid = readIntAttribute(in, ATTR_UID);
final int policy = readIntAttribute(in, ATTR_POLICY);
@@ -1898,9 +1962,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
} catch (FileNotFoundException e) {
// missing policy is okay, probably first boot
upgradeDefaultBackgroundDataUL();
} catch (IOException e) {
Log.wtf(TAG, "problem reading network policy", e);
} catch (XmlPullParserException e) {
} catch (Exception e) {
Log.wtf(TAG, "problem reading network policy", e);
} finally {
IoUtils.closeQuietly(fis);
@@ -1994,8 +2056,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
if (networkId != null) {
out.attribute(null, ATTR_NETWORK_ID, networkId);
}
writeIntAttribute(out, ATTR_CYCLE_DAY, policy.cycleDay);
out.attribute(null, ATTR_CYCLE_TIMEZONE, policy.cycleTimezone);
writeStringAttribute(out, ATTR_CYCLE_START,
RecurrenceRule.convertZonedDateTime(policy.cycleRule.start));
writeStringAttribute(out, ATTR_CYCLE_END,
RecurrenceRule.convertZonedDateTime(policy.cycleRule.end));
writeStringAttribute(out, ATTR_CYCLE_PERIOD,
RecurrenceRule.convertPeriod(policy.cycleRule.period));
writeLongAttribute(out, ATTR_WARNING_BYTES, policy.warningBytes);
writeLongAttribute(out, ATTR_LIMIT_BYTES, policy.limitBytes);
writeLongAttribute(out, ATTR_LAST_WARNING_SNOOZE, policy.lastWarningSnooze);
@@ -2005,6 +2071,32 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
out.endTag(null, TAG_NETWORK_POLICY);
}
// write all known subscription plans
for (int i = 0; i < mSubscriptionPlans.size(); i++) {
final int subId = mSubscriptionPlans.keyAt(i);
final SubscriptionPlan[] plans = mSubscriptionPlans.valueAt(i);
if (ArrayUtils.isEmpty(plans)) continue;
for (SubscriptionPlan plan : plans) {
out.startTag(null, TAG_SUBSCRIPTION_PLAN);
writeIntAttribute(out, ATTR_SUB_ID, subId);
final RecurrenceRule cycleRule = plan.getCycleRule();
writeStringAttribute(out, ATTR_CYCLE_START,
RecurrenceRule.convertZonedDateTime(cycleRule.start));
writeStringAttribute(out, ATTR_CYCLE_END,
RecurrenceRule.convertZonedDateTime(cycleRule.end));
writeStringAttribute(out, ATTR_CYCLE_PERIOD,
RecurrenceRule.convertPeriod(cycleRule.period));
writeStringAttribute(out, ATTR_TITLE, plan.getTitle());
writeStringAttribute(out, ATTR_SUMMARY, plan.getSummary());
writeLongAttribute(out, ATTR_LIMIT_BYTES, plan.getDataLimitBytes());
writeIntAttribute(out, ATTR_LIMIT_BEHAVIOR, plan.getDataLimitBehavior());
writeLongAttribute(out, ATTR_USAGE_BYTES, plan.getDataUsageBytes());
writeLongAttribute(out, ATTR_USAGE_TIME, plan.getDataUsageTime());
out.endTag(null, TAG_SUBSCRIPTION_PLAN);
}
}
// write all known uid policies
for (int i = 0; i < mUidPolicy.size(); i++) {
final int uid = mUidPolicy.keyAt(i);
@@ -2506,27 +2598,35 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
final SubscriptionInfo si;
final PersistableBundle config;
final long token = Binder.clearCallingIdentity();
try {
si = mContext.getSystemService(SubscriptionManager.class)
.getActiveSubscriptionInfo(subId);
config = mCarrierConfigManager.getConfigForSubId(subId);
} finally {
Binder.restoreCallingIdentity(token);
}
// First check: does caller have carrier access?
// First check: is caller the CarrierService?
if (si.isEmbedded() && si.canManageSubscription(mContext, callingPackage)) {
Slog.v(TAG, "Granting access because " + callingPackage + " is carrier");
return;
}
// Second check: was caller first to claim this HNI?
// TODO: extend to support external data sources
// Second check: has the CarrierService delegated access?
if (config != null) {
final String overridePackage = config
.getString(CarrierConfigManager.KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING, null);
if (!TextUtils.isEmpty(overridePackage)
&& Objects.equals(overridePackage, callingPackage)) {
return;
}
}
// Final check: does caller have fallback permission?
if (mContext.checkCallingOrSelfPermission(
MANAGE_FALLBACK_SUBSCRIPTION_PLANS) == PERMISSION_GRANTED) {
Slog.v(TAG, "Granting access because " + callingPackage + " is fallback");
// Third check: is caller the fallback/default CarrierService?
final String defaultPackage = mCarrierConfigManager.getDefaultCarrierServicePackageName();
if (!TextUtils.isEmpty(defaultPackage)
&& Objects.equals(defaultPackage, callingPackage)) {
return;
}
@@ -2538,11 +2638,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
public SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage) {
enforceSubscriptionPlanAccess(subId, Binder.getCallingUid(), callingPackage);
// TODO: extend to support external data sources
if (!"com.android.settings".equals(callingPackage)) {
throw new UnsupportedOperationException();
}
final String fake = SystemProperties.get("fw.fake_plan");
if (!TextUtils.isEmpty(fake)) {
final List<SubscriptionPlan> plans = new ArrayList<>();
@@ -2550,7 +2645,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
plans.add(SubscriptionPlan.Builder
.createRecurringMonthly(ZonedDateTime.parse("2007-03-14T00:00:00.000Z"))
.setTitle("G-Mobile")
.setDataWarning(2 * TrafficStats.GB_IN_BYTES)
.setDataLimit(5 * TrafficStats.GB_IN_BYTES,
SubscriptionPlan.LIMIT_BEHAVIOR_BILLED)
.setDataUsage(1 * TrafficStats.GB_IN_BYTES,
@@ -2562,7 +2656,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
.setTitle("G-Mobile is the carriers name who this plan belongs to")
.setSummary("Crazy unlimited bandwidth plan with incredibly long title "
+ "that should be cut off to prevent UI from looking terrible")
.setDataWarning(2 * TrafficStats.GB_IN_BYTES)
.setDataLimit(5 * TrafficStats.GB_IN_BYTES,
SubscriptionPlan.LIMIT_BEHAVIOR_THROTTLED)
.setDataUsage(1 * TrafficStats.GB_IN_BYTES,
@@ -2615,19 +2708,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
return plans.toArray(new SubscriptionPlan[plans.size()]);
}
final long token = Binder.clearCallingIdentity();
try {
final TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
final NetworkTemplate template = NetworkTemplate
.buildTemplateMobileAll(tm.getSubscriberId(subId));
final NetworkPolicy policy = mNetworkPolicy.get(template);
if (policy != null) {
return new SubscriptionPlan[] { SubscriptionPlan.convert(policy) };
} else {
return new SubscriptionPlan[0];
synchronized (mUidRulesFirstLock) {
synchronized (mNetworkPoliciesSecondLock) {
return mSubscriptionPlans.get(subId);
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
@@ -2635,36 +2719,25 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
public void setSubscriptionPlans(int subId, SubscriptionPlan[] plans, String callingPackage) {
enforceSubscriptionPlanAccess(subId, Binder.getCallingUid(), callingPackage);
// TODO: extend to support external data sources
if (!"com.android.settings".equals(callingPackage)) {
throw new UnsupportedOperationException();
for (SubscriptionPlan plan : plans) {
Preconditions.checkNotNull(plan);
}
final long token = Binder.clearCallingIdentity();
try {
final TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
final NetworkTemplate template = NetworkTemplate
.buildTemplateMobileAll(tm.getSubscriberId(subId));
if (ArrayUtils.isEmpty(plans)) {
mNetworkPolicy.remove(template);
} else {
final NetworkPolicy policy = SubscriptionPlan.convert(plans[0]);
policy.template = template;
mNetworkPolicy.put(template, policy);
maybeRefreshTrustedTime();
synchronized (mUidRulesFirstLock) {
synchronized (mNetworkPoliciesSecondLock) {
mSubscriptionPlans.put(subId, plans);
// TODO: update any implicit details from newly defined plans
handleNetworkPoliciesUpdateAL(false);
}
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public String getSubscriptionPlanOwner(int subId) {
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
// TODO: extend to support external data sources
return "com.android.settings";
}
@Override
protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return;

View File

@@ -34,7 +34,6 @@ import static android.telephony.CarrierConfigManager.DATA_CYCLE_USE_PLATFORM_DEF
import static android.telephony.CarrierConfigManager.KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG;
import static android.telephony.CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_BYTES_LONG;
import static android.telephony.CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static android.text.format.Time.TIMEZONE_UTC;
@@ -92,7 +91,6 @@ import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkPolicy;
import android.net.NetworkPolicyManager;
import android.net.NetworkState;
import android.net.NetworkStats;
import android.net.NetworkTemplate;
@@ -108,12 +106,12 @@ import android.support.test.filters.MediumTest;
import android.support.test.runner.AndroidJUnit4;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionPlan;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.text.format.Time;
import android.util.Log;
import android.util.Pair;
import android.util.RecurrenceRule;
import android.util.TrustedTime;
import com.android.internal.telephony.PhoneConstants;
@@ -154,7 +152,10 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.time.Clock;
import java.time.Instant;
import java.time.Period;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.Calendar;
@@ -396,7 +397,7 @@ public class NetworkPolicyManagerServiceTest {
@After
public void resetClock() throws Exception {
SubscriptionPlan.sNowOverride = -1;
RecurrenceRule.sClock = Clock.systemDefaultZone();
}
@Test
@@ -785,9 +786,9 @@ public class NetworkPolicyManagerServiceTest {
}
private static long computeLastCycleBoundary(long currentTime, NetworkPolicy policy) {
SubscriptionPlan.sNowOverride = currentTime;
final Iterator<Pair<ZonedDateTime, ZonedDateTime>> it = NetworkPolicyManager
.cycleIterator(policy);
RecurrenceRule.sClock = Clock.fixed(Instant.ofEpochMilli(currentTime),
ZoneId.systemDefault());
final Iterator<Pair<ZonedDateTime, ZonedDateTime>> it = policy.cycleIterator();
while (it.hasNext()) {
final Pair<ZonedDateTime, ZonedDateTime> cycle = it.next();
if (cycle.first.toInstant().toEpochMilli() < currentTime) {
@@ -799,8 +800,9 @@ public class NetworkPolicyManagerServiceTest {
}
private static long computeNextCycleBoundary(long currentTime, NetworkPolicy policy) {
SubscriptionPlan.sNowOverride = currentTime;
return NetworkPolicyManager.cycleIterator(policy).next().second.toInstant().toEpochMilli();
RecurrenceRule.sClock = Clock.fixed(Instant.ofEpochMilli(currentTime),
ZoneId.systemDefault());
return policy.cycleIterator().next().second.toInstant().toEpochMilli();
}
@Test
@@ -893,38 +895,6 @@ public class NetworkPolicyManagerServiceTest {
computeNextCycleBoundary(parseTime("2012-01-05T00:00:00.000Z"), policy));
}
@Test
public void testNextCycleSane() throws Exception {
final NetworkPolicy policy = new NetworkPolicy(
sTemplateWifi, 31, TIMEZONE_UTC, WARNING_DISABLED, LIMIT_DISABLED, false);
final LinkedHashSet<Long> seen = new LinkedHashSet<Long>();
// walk forwards, ensuring that cycle boundaries don't get stuck
long currentCycle = computeNextCycleBoundary(parseTime("2011-08-01T00:00:00.000Z"), policy);
for (int i = 0; i < 128; i++) {
long nextCycle = computeNextCycleBoundary(currentCycle, policy);
assertEqualsFuzzy(DAY_IN_MILLIS * 30, nextCycle - currentCycle, DAY_IN_MILLIS * 3);
assertUnique(seen, nextCycle);
currentCycle = nextCycle;
}
}
@Test
public void testLastCycleSane() throws Exception {
final NetworkPolicy policy = new NetworkPolicy(
sTemplateWifi, 31, TIMEZONE_UTC, WARNING_DISABLED, LIMIT_DISABLED, false);
final LinkedHashSet<Long> seen = new LinkedHashSet<Long>();
// walk backwards, ensuring that cycle boundaries look sane
long currentCycle = computeLastCycleBoundary(parseTime("2011-08-04T00:00:00.000Z"), policy);
for (int i = 0; i < 128; i++) {
long lastCycle = computeLastCycleBoundary(currentCycle, policy);
assertEqualsFuzzy(DAY_IN_MILLIS * 30, currentCycle - lastCycle, DAY_IN_MILLIS * 3);
assertUnique(seen, lastCycle);
currentCycle = lastCycle;
}
}
@Test
public void testCycleTodayJanuary() throws Exception {
final NetworkPolicy policy = new NetworkPolicy(
@@ -945,17 +915,6 @@ public class NetworkPolicyManagerServiceTest {
computeLastCycleBoundary(parseTime("2013-01-14T15:11:00.000-08:00"), policy));
}
@Test
public void testLastCycleBoundaryDST() throws Exception {
final long currentTime = parseTime("1989-01-02T07:30:00.000Z");
final long expectedCycle = parseTime("1988-12-03T02:00:00.000Z");
final NetworkPolicy policy = new NetworkPolicy(
sTemplateWifi, 3, "America/Argentina/Buenos_Aires", 1024L, 1024L, false);
final long actualCycle = computeLastCycleBoundary(currentTime, policy);
assertTimeEquals(expectedCycle, actualCycle);
}
@Test
public void testNetworkPolicyAppliedCycleLastMonth() throws Exception {
NetworkState[] state = null;
@@ -1144,15 +1103,6 @@ public class NetworkPolicyManagerServiceTest {
}
}
@Test
public void testConversion() throws Exception {
NetworkTemplate template = NetworkTemplate.buildTemplateMobileWildcard();
NetworkPolicy before = new NetworkPolicy(template, 12, "Israel", 123, 456, true);
NetworkPolicy after = SubscriptionPlan.convert(SubscriptionPlan.convert(before));
after.template = before.template;
assertEquals(before, after);
}
@Test
public void testOnUidStateChanged_notifyAMS() throws Exception {
final long procStateSeq = 222;
@@ -1472,7 +1422,9 @@ public class NetworkPolicyManagerServiceTest {
private NetworkPolicy buildDefaultFakeMobilePolicy() {
NetworkPolicy p = mService.buildDefaultMobilePolicy(FAKE_SUB_ID, FAKE_SUBSCRIBER_ID);
// set a deterministic cycle date
p.cycleDay = DEFAULT_CYCLE_DAY;
p.cycleRule = new RecurrenceRule(
p.cycleRule.start.withDayOfMonth(DEFAULT_CYCLE_DAY),
p.cycleRule.end, Period.ofMonths(1));
return p;
}
@@ -1665,7 +1617,8 @@ public class NetworkPolicyManagerServiceTest {
}
private void setCurrentTimeMillis(long currentTimeMillis) {
SubscriptionPlan.sNowOverride = currentTimeMillis;
RecurrenceRule.sClock = Clock.fixed(Instant.ofEpochMilli(currentTimeMillis),
ZoneId.systemDefault());
mStartTime = currentTimeMillis;
mElapsedRealtime = 0L;
}

View File

@@ -26,6 +26,7 @@ import android.content.Context;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.service.carrier.CarrierService;
import com.android.ims.ImsReasonInfo;
import com.android.internal.telephony.ICarrierConfigLoader;
@@ -261,6 +262,17 @@ public class CarrierConfigManager {
public static final String KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING =
"config_ims_package_override_string";
/**
* Override the package that will manage {@link SubscriptionPlan}
* information instead of the {@link CarrierService} that defines this
* value.
*
* @see SubscriptionManager#getSubscriptionPlans(int)
* @see SubscriptionManager#setSubscriptionPlans(int, java.util.List)
*/
public static final String KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING =
"config_plans_package_override_string";
/**
* Override the platform's notion of a network operator being considered roaming.
* Value is string array of SIDs to be considered roaming for 3GPP2 RATs.
@@ -1379,12 +1391,16 @@ public class CarrierConfigManager {
/**
* The day of the month (1-31) on which the data cycle rolls over.
* <p>
* If the current month does not have this day, the cycle will roll over at the start of the
* next month.
* If the current month does not have this day, the cycle will roll over at
* the start of the next month.
* <p>
* This setting may be still overridden by explicit user choice. By default, the platform value
* will be used.
* This setting may be still overridden by explicit user choice. By default,
* the platform value will be used.
*
* @deprecated replaced by
* {@link SubscriptionManager#setSubscriptionPlans(int, java.util.List)}
*/
@Deprecated
public static final String KEY_MONTHLY_DATA_CYCLE_DAY_INT =
"monthly_data_cycle_day_int";
@@ -1395,6 +1411,7 @@ public class CarrierConfigManager {
*
* @hide
*/
@Deprecated
public static final int DATA_CYCLE_USE_PLATFORM_DEFAULT = -1;
/**
@@ -1408,6 +1425,7 @@ public class CarrierConfigManager {
* default data limit, if one exists, will be disabled. A user selected data limit will not be
* overridden.
*/
@Deprecated
public static final int DATA_CYCLE_THRESHOLD_DISABLED = -2;
/**
@@ -1420,7 +1438,11 @@ public class CarrierConfigManager {
* <p>
* This setting may be overridden by explicit user choice. By default, the platform value
* will be used.
*
* @deprecated replaced by
* {@link SubscriptionManager#setSubscriptionPlans(int, java.util.List)}
*/
@Deprecated
public static final String KEY_DATA_WARNING_THRESHOLD_BYTES_LONG =
"data_warning_threshold_bytes_long";
@@ -1434,7 +1456,11 @@ public class CarrierConfigManager {
* <p>
* This setting may be overridden by explicit user choice. By default, the platform value
* will be used.
*
* @deprecated replaced by
* {@link SubscriptionManager#setSubscriptionPlans(int, java.util.List)}
*/
@Deprecated
public static final String KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG =
"data_limit_threshold_bytes_long";
@@ -1866,6 +1892,15 @@ public class CarrierConfigManager {
}
}
/** {@hide} */
public String getDefaultCarrierServicePackageName() {
try {
return getICarrierConfigLoader().getDefaultCarrierServicePackageName();
} catch (Throwable t) {
return null;
}
}
/**
* Returns a new bundle with the default value for every supported configuration variable.
*

View File

@@ -18,8 +18,8 @@ package android.telephony;
import android.annotation.NonNull;
import android.annotation.SdkConstant;
import android.annotation.SystemService;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemService;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
@@ -1542,7 +1542,20 @@ public class SubscriptionManager {
return false;
}
/** {@pending} */
/**
* Get the description of the billing relationship plan between a carrier
* and a specific subscriber.
* <p>
* This method is only accessible to the following narrow set of apps:
* <ul>
* <li>The carrier app for this subscriberId, as determined by
* {@link TelephonyManager#hasCarrierPrivileges(int)}.
* <li>The carrier app explicitly delegated access through
* {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}.
* </ul>
*
* @param subId the subscriber this relationship applies to
*/
public @NonNull List<SubscriptionPlan> getSubscriptionPlans(int subId) {
final INetworkPolicyManager npm = INetworkPolicyManager.Stub
.asInterface(ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
@@ -1554,7 +1567,23 @@ public class SubscriptionManager {
}
}
/** {@pending} */
/**
* Set the description of the billing relationship plan between a carrier
* and a specific subscriber.
* <p>
* This method is only accessible to the following narrow set of apps:
* <ul>
* <li>The carrier app for this subscriberId, as determined by
* {@link TelephonyManager#hasCarrierPrivileges(int)}.
* <li>The carrier app explicitly delegated access through
* {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}.
* </ul>
*
* @param subId the subscriber this relationship applies to
* @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.
*/
public void setSubscriptionPlans(int subId, @NonNull List<SubscriptionPlan> plans) {
final INetworkPolicyManager npm = INetworkPolicyManager.Stub
.asInterface(ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
@@ -1565,15 +1594,4 @@ public class SubscriptionManager {
throw e.rethrowFromSystemServer();
}
}
/** {@hide} */
public String getSubscriptionPlanOwner(int subId) {
final INetworkPolicyManager npm = INetworkPolicyManager.Stub
.asInterface(ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
try {
return npm.getSubscriptionPlanOwner(subId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}

View File

@@ -19,45 +19,31 @@ 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.net.NetworkPolicy;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
import android.util.Pair;
import android.util.RecurrenceRule;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.time.Instant;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.Period;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalUnit;
import java.util.Iterator;
/** {@pending} */
/**
* 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.
*
* @see SubscriptionManager#setSubscriptionPlans(int, java.util.List)
* @see SubscriptionManager#getSubscriptionPlans(int)
*/
public final class SubscriptionPlan implements Parcelable {
private static final String TAG = "SubscriptionPlan";
private static final boolean DEBUG = false;
/** {@hide} */
@IntDef(prefix = "TYPE_", value = {
TYPE_NONRECURRING,
TYPE_RECURRING_WEEKLY,
TYPE_RECURRING_MONTHLY,
TYPE_RECURRING_DAILY,
})
@Retention(RetentionPolicy.SOURCE)
public @interface Type {}
public static final int TYPE_NONRECURRING = 0;
public static final int TYPE_RECURRING_MONTHLY = 1;
public static final int TYPE_RECURRING_WEEKLY = 2;
public static final int TYPE_RECURRING_DAILY = 3;
/** {@hide} */
@IntDef(prefix = "LIMIT_BEHAVIOR_", value = {
LIMIT_BEHAVIOR_UNKNOWN,
@@ -68,51 +54,40 @@ public final class SubscriptionPlan implements Parcelable {
@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 int type;
private final ZonedDateTime start;
private final ZonedDateTime end;
private final RecurrenceRule cycleRule;
private CharSequence title;
private CharSequence summary;
private long dataWarningBytes = BYTES_UNKNOWN;
private long dataWarningSnoozeTime = TIME_UNKNOWN;
private long dataLimitBytes = BYTES_UNKNOWN;
private long dataLimitSnoozeTime = TIME_UNKNOWN;
private int dataLimitBehavior = LIMIT_BEHAVIOR_UNKNOWN;
private long dataUsageBytes = BYTES_UNKNOWN;
private long dataUsageTime = TIME_UNKNOWN;
private SubscriptionPlan(@Type int type, ZonedDateTime start, ZonedDateTime end) {
this.type = type;
this.start = start;
this.end = end;
private SubscriptionPlan(RecurrenceRule cycleRule) {
this.cycleRule = Preconditions.checkNotNull(cycleRule);
}
private SubscriptionPlan(Parcel source) {
type = source.readInt();
if (source.readInt() != 0) {
start = ZonedDateTime.parse(source.readString());
} else {
start = null;
}
if (source.readInt() != 0) {
end = ZonedDateTime.parse(source.readString());
} else {
end = null;
}
cycleRule = source.readParcelable(null);
title = source.readCharSequence();
summary = source.readCharSequence();
dataWarningBytes = source.readLong();
dataWarningSnoozeTime = source.readLong();
dataLimitBytes = source.readLong();
dataLimitSnoozeTime = source.readLong();
dataLimitBehavior = source.readInt();
dataUsageBytes = source.readLong();
dataUsageTime = source.readLong();
@@ -125,25 +100,10 @@ public final class SubscriptionPlan implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(type);
if (start != null) {
dest.writeInt(1);
dest.writeString(start.toString());
} else {
dest.writeInt(0);
}
if (end != null) {
dest.writeInt(1);
dest.writeString(end.toString());
} else {
dest.writeInt(0);
}
dest.writeParcelable(cycleRule, flags);
dest.writeCharSequence(title);
dest.writeCharSequence(summary);
dest.writeLong(dataWarningBytes);
dest.writeLong(dataWarningSnoozeTime);
dest.writeLong(dataLimitBytes);
dest.writeLong(dataLimitSnoozeTime);
dest.writeInt(dataLimitBehavior);
dest.writeLong(dataUsageBytes);
dest.writeLong(dataUsageTime);
@@ -151,20 +111,15 @@ public final class SubscriptionPlan implements Parcelable {
@Override
public String toString() {
return new StringBuilder("SubscriptionPlan:")
.append(" type=").append(type)
.append(" start=").append(start)
.append(" end=").append(end)
return new StringBuilder("SubscriptionPlan{")
.append("cycleRule=").append(cycleRule)
.append(" title=").append(title)
.append(" summary=").append(summary)
.append(" dataWarningBytes=").append(dataWarningBytes)
.append(" dataWarningSnoozeTime=").append(dataWarningSnoozeTime)
.append(" dataLimitBytes=").append(dataLimitBytes)
.append(" dataLimitSnoozeTime=").append(dataLimitSnoozeTime)
.append(" dataLimitBehavior=").append(dataLimitBehavior)
.append(" dataUsageBytes=").append(dataUsageBytes)
.append(" dataUsageTime=").append(dataUsageTime)
.toString();
.append("}").toString();
}
public static final Parcelable.Creator<SubscriptionPlan> CREATOR = new Parcelable.Creator<SubscriptionPlan>() {
@@ -179,296 +134,161 @@ public final class SubscriptionPlan implements Parcelable {
}
};
public @Type int getType() {
return type;
}
public ZonedDateTime getStart() {
return start;
}
public ZonedDateTime getEnd() {
return end;
/** {@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;
}
public @BytesLong long getDataWarningBytes() {
return dataWarningBytes;
}
/**
* 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;
}
/** {@hide} */
@VisibleForTesting
public static long sNowOverride = -1;
private static ZonedDateTime now(ZoneId zone) {
return (sNowOverride != -1)
? ZonedDateTime.ofInstant(Instant.ofEpochMilli(sNowOverride), zone)
: ZonedDateTime.now(zone);
}
/** {@hide} */
public static SubscriptionPlan convert(NetworkPolicy policy) {
final ZoneId zone = ZoneId.of(policy.cycleTimezone);
final ZonedDateTime now = now(zone);
final Builder builder;
if (policy.cycleDay != NetworkPolicy.CYCLE_NONE) {
// Assume we started last January, since it has all possible days
ZonedDateTime start = ZonedDateTime.of(
now.toLocalDate().minusYears(1).withMonth(1).withDayOfMonth(policy.cycleDay),
LocalTime.MIDNIGHT, zone);
builder = Builder.createRecurringMonthly(start);
} else {
Log.w(TAG, "Cycle not defined; assuming last 4 weeks non-recurring");
ZonedDateTime end = now;
ZonedDateTime start = end.minusWeeks(4);
builder = Builder.createNonrecurring(start, end);
}
if (policy.warningBytes != NetworkPolicy.WARNING_DISABLED) {
builder.setDataWarning(policy.warningBytes);
}
if (policy.lastWarningSnooze != NetworkPolicy.SNOOZE_NEVER) {
builder.setDataWarningSnooze(policy.lastWarningSnooze);
}
if (policy.limitBytes != NetworkPolicy.LIMIT_DISABLED) {
builder.setDataLimit(policy.limitBytes, LIMIT_BEHAVIOR_DISABLED);
}
if (policy.lastLimitSnooze != NetworkPolicy.SNOOZE_NEVER) {
builder.setDataLimitSnooze(policy.lastLimitSnooze);
}
return builder.build();
}
/** {@hide} */
public static NetworkPolicy convert(SubscriptionPlan plan) {
final NetworkPolicy policy = new NetworkPolicy();
switch (plan.type) {
case TYPE_RECURRING_MONTHLY:
policy.cycleDay = plan.start.getDayOfMonth();
policy.cycleTimezone = plan.start.getZone().getId();
break;
default:
policy.cycleDay = NetworkPolicy.CYCLE_NONE;
policy.cycleTimezone = "UTC";
break;
}
policy.warningBytes = plan.dataWarningBytes;
policy.limitBytes = plan.dataLimitBytes;
policy.lastWarningSnooze = plan.dataWarningSnoozeTime;
policy.lastLimitSnooze = plan.dataLimitSnoozeTime;
policy.metered = true;
policy.inferred = false;
return policy;
}
/** {@hide} */
public TemporalUnit getTemporalUnit() {
switch (type) {
case TYPE_RECURRING_DAILY: return ChronoUnit.DAYS;
case TYPE_RECURRING_WEEKLY: return ChronoUnit.WEEKS;
case TYPE_RECURRING_MONTHLY: return ChronoUnit.MONTHS;
default: throw new IllegalArgumentException();
}
/**
* 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<Pair<ZonedDateTime, ZonedDateTime>> cycleIterator() {
return cycleRule.cycleIterator();
}
/**
* Return an iterator that returns data usage cycles.
* <p>
* For recurring plans, it starts at the currently active cycle, and then
* walks backwards in time through each previous cycle, back to the defined
* starting point and no further.
* <p>
* For non-recurring plans, it returns one single cycle.
* Builder for a {@link SubscriptionPlan}.
*/
public Iterator<Pair<ZonedDateTime, ZonedDateTime>> cycleIterator() {
switch (type) {
case TYPE_NONRECURRING:
return new NonrecurringIterator();
case TYPE_RECURRING_WEEKLY:
case TYPE_RECURRING_MONTHLY:
case TYPE_RECURRING_DAILY:
return new RecurringIterator();
default:
throw new IllegalStateException("Unknown type: " + type);
}
}
private class NonrecurringIterator implements Iterator<Pair<ZonedDateTime, ZonedDateTime>> {
boolean hasNext = true;
@Override
public boolean hasNext() {
return hasNext;
}
@Override
public Pair<ZonedDateTime, ZonedDateTime> next() {
hasNext = false;
return new Pair<>(start, end);
}
}
private class RecurringIterator implements Iterator<Pair<ZonedDateTime, ZonedDateTime>> {
TemporalUnit unit;
long i;
ZonedDateTime cycleStart;
ZonedDateTime cycleEnd;
public RecurringIterator() {
final ZonedDateTime now = now(start.getZone());
if (DEBUG) Log.d(TAG, "Resolving using now " + now);
unit = getTemporalUnit();
i = unit.between(start, now);
updateCycle();
// Walk forwards until we find first cycle after now
while (cycleEnd.toEpochSecond() <= now.toEpochSecond()) {
i++;
updateCycle();
}
// Walk backwards until we find first cycle before now
while (cycleStart.toEpochSecond() > now.toEpochSecond()) {
i--;
updateCycle();
}
}
private void updateCycle() {
cycleStart = roundBoundaryTime(start.plus(i, unit));
cycleEnd = roundBoundaryTime(start.plus(i + 1, unit));
}
private ZonedDateTime roundBoundaryTime(ZonedDateTime boundary) {
if ((type == TYPE_RECURRING_MONTHLY)
&& (boundary.getDayOfMonth() < start.getDayOfMonth())) {
// When forced to end a monthly cycle early, we want to count
// that entire day against the boundary.
return ZonedDateTime.of(boundary.toLocalDate(), LocalTime.MAX, start.getZone());
} else {
return boundary;
}
}
@Override
public boolean hasNext() {
return cycleStart.toEpochSecond() >= start.toEpochSecond();
}
@Override
public Pair<ZonedDateTime, ZonedDateTime> next() {
if (DEBUG) Log.d(TAG, "Cycle " + i + " from " + cycleStart + " to " + cycleEnd);
Pair<ZonedDateTime, ZonedDateTime> p = new Pair<>(cycleStart, cycleEnd);
i--;
updateCycle();
return p;
}
}
public static class Builder {
private final SubscriptionPlan plan;
private Builder(@Type int type, ZonedDateTime start, ZonedDateTime end) {
plan = new SubscriptionPlan(type, start, end);
/** {@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.
*/
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(TYPE_NONRECURRING, start, end);
return new Builder(start, end, null);
}
/**
* 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.
*/
public static Builder createRecurringMonthly(ZonedDateTime start) {
return new Builder(TYPE_RECURRING_MONTHLY, start, null);
return new Builder(start, null, Period.ofMonths(1));
}
/**
* Start defining a {@link SubscriptionPlan} that will recur
* automatically every week.
*/
public static Builder createRecurringWeekly(ZonedDateTime start) {
return new Builder(TYPE_RECURRING_WEEKLY, start, null);
return new Builder(start, null, Period.ofDays(7));
}
/**
* Start defining a {@link SubscriptionPlan} that will recur
* automatically every day.
*/
public static Builder createRecurringDaily(ZonedDateTime start) {
return new Builder(TYPE_RECURRING_DAILY, start, null);
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;
}
public Builder setDataWarning(@BytesLong long dataWarningBytes) {
if (dataWarningBytes < BYTES_UNKNOWN) {
throw new IllegalArgumentException("Warning must be positive or BYTES_UNKNOWN");
}
plan.dataWarningBytes = dataWarningBytes;
return this;
}
/** {@hide} */
public Builder setDataWarningSnooze(@CurrentTimeMillisLong long dataWarningSnoozeTime) {
plan.dataWarningSnoozeTime = dataWarningSnoozeTime;
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 < BYTES_UNKNOWN) {
throw new IllegalArgumentException("Limit must be positive or BYTES_UNKNOWN");
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;
}
/** {@hide} */
public Builder setDataLimitSnooze(@CurrentTimeMillisLong long dataLimitSnoozeTime) {
plan.dataLimitSnoozeTime = dataLimitSnoozeTime;
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 < BYTES_UNKNOWN) {
throw new IllegalArgumentException("Usage must be positive or BYTES_UNKNOWN");
if (dataUsageBytes < 0) {
throw new IllegalArgumentException("Usage bytes must be positive");
}
if (dataUsageTime < TIME_UNKNOWN) {
throw new IllegalArgumentException("Time must be positive or TIME_UNKNOWN");
}
if ((dataUsageBytes == BYTES_UNKNOWN) != (dataUsageTime == TIME_UNKNOWN)) {
throw new IllegalArgumentException("Must provide both usage and time or neither");
if (dataUsageTime < 0) {
throw new IllegalArgumentException("Usage time must be positive");
}
plan.dataUsageBytes = dataUsageBytes;
plan.dataUsageTime = dataUsageTime;

View File

@@ -28,4 +28,7 @@ interface ICarrierConfigLoader {
void notifyConfigChangedForSubId(int subId);
void updateConfigForPhoneId(int phoneId, String simState);
String getDefaultCarrierServicePackageName();
}