diff options
| author | 2017-07-21 17:14:30 +0000 | |
|---|---|---|
| committer | 2017-07-21 17:14:30 +0000 | |
| commit | 84c6684bba418c1e902e3bb57c61908ba1a99d6c (patch) | |
| tree | 440412eaf90f8506a44b5a4b00b80b32a8be2eee | |
| parent | 94e56bc598fe74ea7113199778830186443de850 (diff) | |
| parent | 17bebd2fd7d3af0bc648172da6ae36f8b55ad7af (diff) | |
Merge "Expose SubscriptionPlan API." into oc-mr1-dev
17 files changed, 977 insertions, 544 deletions
diff --git a/api/current.txt b/api/current.txt index 15354357e293..72e70a01502b 100644 --- a/api/current.txt +++ b/api/current.txt @@ -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); diff --git a/api/system-current.txt b/api/system-current.txt index 8e3c840fa8ab..31b295da8ff3 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -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); diff --git a/api/test-current.txt b/api/test-current.txt index cbf8e0610967..a58b2517ad50 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -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); diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl index 7b948a7a933e..181e4a26d9b8 100644 --- a/core/java/android/net/INetworkPolicyManager.aidl +++ b/core/java/android/net/INetworkPolicyManager.aidl @@ -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); } diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java index edf9a28d27f5..5df742c5a394 100644 --- a/core/java/android/net/NetworkPolicy.java +++ b/core/java/android/net/NetworkPolicy.java @@ -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); } } diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 3fe9b0d74538..81c49a339d53 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -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(); } /** diff --git a/core/java/android/util/RecurrenceRule.java b/core/java/android/util/RecurrenceRule.java new file mode 100644 index 000000000000..1fe638d6b76b --- /dev/null +++ b/core/java/android/util/RecurrenceRule.java @@ -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; + } +} diff --git a/core/java/com/android/internal/util/XmlUtils.java b/core/java/com/android/internal/util/XmlUtils.java index 3d8c85e684cb..3188d3005cae 100644 --- a/core/java/com/android/internal/util/XmlUtils.java +++ b/core/java/com/android/internal/util/XmlUtils.java @@ -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()); } } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index c052e5145190..4a2ef3228d5e 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -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" /> diff --git a/core/tests/coretests/src/android/util/RecurrenceRuleTest.java b/core/tests/coretests/src/android/util/RecurrenceRuleTest.java new file mode 100644 index 000000000000..42b6048b533a --- /dev/null +++ b/core/tests/coretests/src/android/util/RecurrenceRuleTest.java @@ -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); + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/NetworkPolicyEditor.java b/packages/SettingsLib/src/com/android/settingslib/NetworkPolicyEditor.java index c346898e2911..b01fc8541957 100644 --- a/packages/SettingsLib/src/com/android/settingslib/NetworkPolicyEditor.java +++ b/packages/SettingsLib/src/com/android/settingslib/NetworkPolicyEditor.java @@ -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(); diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 38f1c076727f..f70486a8b889 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -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,22 +2719,19 @@ 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); @@ -2658,14 +2739,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } @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; diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java index 11e383c25ef0..8decb968c8fb 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java @@ -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 @@ -894,38 +896,6 @@ public class NetworkPolicyManagerServiceTest { } @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( sTemplateWifi, 14, "US/Pacific", 1024L, 1024L, false); @@ -946,17 +916,6 @@ public class NetworkPolicyManagerServiceTest { } @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; NetworkStats stats = null; @@ -1145,15 +1104,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; callOnUidStateChanged(UID_A, ActivityManager.PROCESS_STATE_SERVICE, procStateSeq); @@ -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; } diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 8b3a804d35f9..0001d42f4329 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -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; @@ -262,6 +263,17 @@ public class CarrierConfigManager { "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. * diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 89c9134f2be2..503bf820c9a0 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -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(); - } - } } diff --git a/telephony/java/android/telephony/SubscriptionPlan.java b/telephony/java/android/telephony/SubscriptionPlan.java index da7661aeb787..c9419c535b82 100644 --- a/telephony/java/android/telephony/SubscriptionPlan.java +++ b/telephony/java/android/telephony/SubscriptionPlan.java @@ -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 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. + * 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() { - 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; - } + return cycleRule.cycleIterator(); } + /** + * Builder for a {@link SubscriptionPlan}. + */ 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 (dataUsageTime < TIME_UNKNOWN) { - throw new IllegalArgumentException("Time must be positive or TIME_UNKNOWN"); + if (dataUsageBytes < 0) { + throw new IllegalArgumentException("Usage bytes must be positive"); } - 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; diff --git a/telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl b/telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl index d77b27f8295a..511573170bb0 100644 --- a/telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl +++ b/telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl @@ -28,4 +28,7 @@ interface ICarrierConfigLoader { void notifyConfigChangedForSubId(int subId); void updateConfigForPhoneId(int phoneId, String simState); + + String getDefaultCarrierServicePackageName(); + } |