OSDN Git Service

APIs to disable data usage notifications.
authorJeff Sharkey <jsharkey@android.com>
Tue, 10 Apr 2018 18:38:29 +0000 (12:38 -0600)
committerJeff Sharkey <jsharkey@google.com>
Wed, 11 Apr 2018 22:07:40 +0000 (22:07 +0000)
Certain carriers have requested the ability to disable the warning,
limit, and rapid data usage notifications shown by the OS, so give
them that ability on a per-subId basis.  (The notifications are still
enabled by default.)

Unhide NOT_CONGESTED to match setSubscriptionOverrideCongested().

Fix bug in getPrimarySubscriptionPlanLocked() to find the first
currently active plan; the first non-recurring plan may be no longer
active.  Fix bug in SubscriptionPlan which allowed invalid negative
or zero-length Period objects.

Bug: 778516917750688277662747
Test: atest com.android.server.NetworkPolicyManagerServiceTest
Test: atest android.telephony.cts.SubscriptionManagerTest
Exempt-From-Owner-Approval: SDK deadline, responding to API feedback
Change-Id: Ib6e6dbadd5022937c3f0661ab4b66aa617f0b5af

api/current.txt
core/java/android/net/NetworkCapabilities.java
core/java/android/util/RecurrenceRule.java
services/core/java/com/android/server/net/NetworkPolicyManagerService.java
services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
telephony/java/android/telephony/CarrierConfigManager.java
telephony/java/android/telephony/SubscriptionPlan.java

index 423e0ae..7408bdb 100644 (file)
@@ -27202,6 +27202,7 @@ package android.net {
     field public static final int NET_CAPABILITY_IMS = 4; // 0x4
     field public static final int NET_CAPABILITY_INTERNET = 12; // 0xc
     field public static final int NET_CAPABILITY_MMS = 0; // 0x0
+    field public static final int NET_CAPABILITY_NOT_CONGESTED = 20; // 0x14
     field public static final int NET_CAPABILITY_NOT_METERED = 11; // 0xb
     field public static final int NET_CAPABILITY_NOT_RESTRICTED = 13; // 0xd
     field public static final int NET_CAPABILITY_NOT_ROAMING = 18; // 0x12
@@ -41682,7 +41683,10 @@ package android.telephony {
     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_CONFIG_TELEPHONY_USE_OWN_NUMBER_FOR_VOICEMAIL_BOOL = "config_telephony_use_own_number_for_voicemail_bool";
     field public static final java.lang.String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool";
+    field public static final java.lang.String KEY_DATA_LIMIT_NOTIFICATION_BOOL = "data_limit_notification_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_RAPID_NOTIFICATION_BOOL = "data_rapid_notification_bool";
+    field public static final java.lang.String KEY_DATA_WARNING_NOTIFICATION_BOOL = "data_warning_notification_bool";
     field public static final 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";
index c314a35..a8e8179 100644 (file)
@@ -254,9 +254,8 @@ public final class NetworkCapabilities implements Parcelable {
     /**
      * Indicates that this network is not congested.
      * <p>
-     * When a network is congested, the device should defer network traffic that
-     * can be done at a later time without breaking developer contracts.
-     * @hide
+     * When a network is congested, applications should defer network traffic
+     * that can be done at a later time, such as uploading analytics.
      */
     public static final int NET_CAPABILITY_NOT_CONGESTED = 20;
 
index 975ad48..9c89876 100644 (file)
@@ -149,6 +149,10 @@ public class RecurrenceRule implements Parcelable {
         }
     };
 
+    public boolean isRecurring() {
+        return period != null;
+    }
+
     @Deprecated
     public boolean isMonthly() {
         return start != null
index 8e77373..0c6555b 100644 (file)
@@ -80,6 +80,9 @@ import static android.provider.Settings.Global.NETPOLICY_QUOTA_UNLIMITED;
 import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
 import static android.telephony.CarrierConfigManager.DATA_CYCLE_THRESHOLD_DISABLED;
 import static android.telephony.CarrierConfigManager.DATA_CYCLE_USE_PLATFORM_DEFAULT;
+import static android.telephony.CarrierConfigManager.KEY_DATA_LIMIT_NOTIFICATION_BOOL;
+import static android.telephony.CarrierConfigManager.KEY_DATA_RAPID_NOTIFICATION_BOOL;
+import static android.telephony.CarrierConfigManager.KEY_DATA_WARNING_NOTIFICATION_BOOL;
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
 import static com.android.internal.util.ArrayUtils.appendInt;
@@ -1093,8 +1096,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
         final long now = mClock.millis();
         for (int i = mNetworkPolicy.size()-1; i >= 0; i--) {
             final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
+            final int subId = findRelevantSubId(policy.template);
+
             // ignore policies that aren't relevant to user
-            if (!isTemplateRelevant(policy.template)) continue;
+            if (subId == INVALID_SUBSCRIPTION_ID) continue;
             if (!policy.hasCycle()) continue;
 
             final Pair<ZonedDateTime, ZonedDateTime> cycle = NetworkPolicyManager
@@ -1103,28 +1108,43 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
             final long cycleEnd = cycle.second.toInstant().toEpochMilli();
             final long totalBytes = getTotalBytes(policy.template, cycleStart, cycleEnd);
 
-            // Notify when data usage is over warning/limit
-            if (policy.isOverLimit(totalBytes)) {
-                final boolean snoozedThisCycle = policy.lastLimitSnooze >= cycleStart;
-                if (snoozedThisCycle) {
-                    enqueueNotification(policy, TYPE_LIMIT_SNOOZED, totalBytes, null);
-                } else {
-                    enqueueNotification(policy, TYPE_LIMIT, totalBytes, null);
-                    notifyOverLimitNL(policy.template);
+            // Carrier might want to manage notifications themselves
+            final PersistableBundle config = mCarrierConfigManager.getConfigForSubId(subId);
+            final boolean notifyWarning = getBooleanDefeatingNullable(config,
+                    KEY_DATA_WARNING_NOTIFICATION_BOOL, true);
+            final boolean notifyLimit = getBooleanDefeatingNullable(config,
+                    KEY_DATA_LIMIT_NOTIFICATION_BOOL, true);
+            final boolean notifyRapid = getBooleanDefeatingNullable(config,
+                    KEY_DATA_RAPID_NOTIFICATION_BOOL, true);
+
+            // Notify when data usage is over warning
+            if (notifyWarning) {
+                if (policy.isOverWarning(totalBytes) && !policy.isOverLimit(totalBytes)) {
+                    final boolean snoozedThisCycle = policy.lastWarningSnooze >= cycleStart;
+                    if (!snoozedThisCycle) {
+                        enqueueNotification(policy, TYPE_WARNING, totalBytes, null);
+                    }
                 }
+            }
 
-            } else {
-                notifyUnderLimitNL(policy.template);
-
-                final boolean snoozedThisCycle = policy.lastWarningSnooze >= cycleStart;
-                if (policy.isOverWarning(totalBytes) && !snoozedThisCycle) {
-                    enqueueNotification(policy, TYPE_WARNING, totalBytes, null);
+            // Notify when data usage is over limit
+            if (notifyLimit) {
+                if (policy.isOverLimit(totalBytes)) {
+                    final boolean snoozedThisCycle = policy.lastLimitSnooze >= cycleStart;
+                    if (snoozedThisCycle) {
+                        enqueueNotification(policy, TYPE_LIMIT_SNOOZED, totalBytes, null);
+                    } else {
+                        enqueueNotification(policy, TYPE_LIMIT, totalBytes, null);
+                        notifyOverLimitNL(policy.template);
+                    }
+                } else {
+                    notifyUnderLimitNL(policy.template);
                 }
             }
 
             // Warn if average usage over last 4 days is on track to blow pretty
             // far past the plan limits.
-            if (policy.limitBytes != LIMIT_DISABLED) {
+            if (notifyRapid && policy.limitBytes != LIMIT_DISABLED) {
                 final long recentDuration = TimeUnit.DAYS.toMillis(4);
                 final long recentStart = now - recentDuration;
                 final long recentEnd = now;
@@ -1201,27 +1221,26 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
      * current device state, such as when
      * {@link TelephonyManager#getSubscriberId()} matches. This is regardless of
      * data connection status.
+     *
+     * @return relevant subId, or {@link #INVALID_SUBSCRIPTION_ID} when no
+     *         matching subId found.
      */
-    private boolean isTemplateRelevant(NetworkTemplate template) {
-        if (template.isMatchRuleMobile()) {
-            final TelephonyManager tele = mContext.getSystemService(TelephonyManager.class);
-            final SubscriptionManager sub = mContext.getSystemService(SubscriptionManager.class);
+    private int findRelevantSubId(NetworkTemplate template) {
+        final TelephonyManager tele = mContext.getSystemService(TelephonyManager.class);
+        final SubscriptionManager sub = mContext.getSystemService(SubscriptionManager.class);
 
-            // Mobile template is relevant when any active subscriber matches
-            final int[] subIds = ArrayUtils.defeatNullable(sub.getActiveSubscriptionIdList());
-            for (int subId : subIds) {
-                final String subscriberId = tele.getSubscriberId(subId);
-                final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
-                        TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true,
-                        true);
-                if (template.matches(probeIdent)) {
-                    return true;
-                }
+        // Mobile template is relevant when any active subscriber matches
+        final int[] subIds = ArrayUtils.defeatNullable(sub.getActiveSubscriptionIdList());
+        for (int subId : subIds) {
+            final String subscriberId = tele.getSubscriberId(subId);
+            final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
+                    TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true,
+                    true);
+            if (template.matches(probeIdent)) {
+                return subId;
             }
-            return false;
-        } else {
-            return true;
         }
+        return INVALID_SUBSCRIPTION_ID;
     }
 
     /**
@@ -3086,9 +3105,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
 
         // We can only override when carrier told us about plans
         synchronized (mNetworkPoliciesSecondLock) {
-            if (ArrayUtils.isEmpty(mSubscriptionPlans.get(subId))) {
+            final SubscriptionPlan plan = getPrimarySubscriptionPlanLocked(subId);
+            if (plan == null
+                    || plan.getDataLimitBehavior() == SubscriptionPlan.LIMIT_BEHAVIOR_UNKNOWN) {
                 throw new IllegalStateException(
-                        "Must provide SubscriptionPlan information before overriding");
+                        "Must provide valid SubscriptionPlan to enable overriding");
             }
         }
 
@@ -4831,7 +4852,21 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
     @GuardedBy("mNetworkPoliciesSecondLock")
     private SubscriptionPlan getPrimarySubscriptionPlanLocked(int subId) {
         final SubscriptionPlan[] plans = mSubscriptionPlans.get(subId);
-        return ArrayUtils.isEmpty(plans) ? null : plans[0];
+        if (!ArrayUtils.isEmpty(plans)) {
+            for (SubscriptionPlan plan : plans) {
+                if (plan.getCycleRule().isRecurring()) {
+                    // Recurring plans will always have an active cycle
+                    return plan;
+                } else {
+                    // Non-recurring plans need manual test for active cycle
+                    final Range<ZonedDateTime> cycle = plan.cycleIterator().next();
+                    if (cycle.contains(ZonedDateTime.now(mClock))) {
+                        return plan;
+                    }
+                }
+            }
+        }
+        return null;
     }
 
     /**
@@ -4878,6 +4913,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
         return (val != null) ? val : new NetworkState[0];
     }
 
+    private static boolean getBooleanDefeatingNullable(@Nullable PersistableBundle bundle,
+            String key, boolean defaultValue) {
+        return (bundle != null) ? bundle.getBoolean(key, defaultValue) : defaultValue;
+    }
+
     private class NotificationId {
         private final String mTag;
         private final int mId;
index cecc94b..b4dc941 100644 (file)
@@ -1714,6 +1714,8 @@ public class NetworkPolicyManagerServiceTest {
     }
 
     private void expectNetworkState(boolean roaming) throws Exception {
+        when(mCarrierConfigManager.getConfigForSubId(eq(TEST_SUB_ID)))
+                .thenReturn(CarrierConfigManager.getDefaultConfig());
         when(mConnManager.getAllNetworkState()).thenReturn(new NetworkState[] {
                 new NetworkState(buildNetworkInfo(),
                         buildLinkProperties(TEST_IFACE),
index aa76eab..e244131 100644 (file)
@@ -1684,6 +1684,14 @@ public class CarrierConfigManager {
             "data_warning_threshold_bytes_long";
 
     /**
+     * Controls if the device should automatically notify the user as they reach
+     * their cellular data warning. When set to {@code false} the carrier is
+     * expected to have implemented their own notification mechanism.
+     */
+    public static final String KEY_DATA_WARNING_NOTIFICATION_BOOL =
+            "data_warning_notification_bool";
+
+    /**
      * Controls the cellular data limit.
      * <p>
      * If the user uses more than this amount of data in their billing cycle, as defined by
@@ -1698,6 +1706,22 @@ public class CarrierConfigManager {
             "data_limit_threshold_bytes_long";
 
     /**
+     * Controls if the device should automatically notify the user as they reach
+     * their cellular data limit. When set to {@code false} the carrier is
+     * expected to have implemented their own notification mechanism.
+     */
+    public static final String KEY_DATA_LIMIT_NOTIFICATION_BOOL =
+            "data_limit_notification_bool";
+
+    /**
+     * Controls if the device should automatically notify the user when rapid
+     * cellular data usage is observed. When set to {@code false} the carrier is
+     * expected to have implemented their own notification mechanism.
+     */
+    public static final String KEY_DATA_RAPID_NOTIFICATION_BOOL =
+            "data_rapid_notification_bool";
+
+    /**
      * Offset to be reduced from rsrp threshold while calculating signal strength level.
      * @hide
      */
@@ -2165,7 +2189,10 @@ public class CarrierConfigManager {
 
         sDefaults.putInt(KEY_MONTHLY_DATA_CYCLE_DAY_INT, DATA_CYCLE_USE_PLATFORM_DEFAULT);
         sDefaults.putLong(KEY_DATA_WARNING_THRESHOLD_BYTES_LONG, DATA_CYCLE_USE_PLATFORM_DEFAULT);
+        sDefaults.putBoolean(KEY_DATA_WARNING_NOTIFICATION_BOOL, true);
         sDefaults.putLong(KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG, DATA_CYCLE_USE_PLATFORM_DEFAULT);
+        sDefaults.putBoolean(KEY_DATA_LIMIT_NOTIFICATION_BOOL, true);
+        sDefaults.putBoolean(KEY_DATA_RAPID_NOTIFICATION_BOOL, true);
 
         // Rat families: {GPRS, EDGE}, {EVDO, EVDO_A, EVDO_B}, {UMTS, HSPA, HSDPA, HSUPA, HSPAP},
         // {LTE, LTE_CA}
index ef2a364..e8bbe42 100644 (file)
@@ -254,6 +254,9 @@ public final class SubscriptionPlan implements Parcelable {
          * @param period The period after which the plan automatically recurs.
          */
         public static Builder createRecurring(ZonedDateTime start, Period period) {
+            if (period.isZero() || period.isNegative()) {
+                throw new IllegalArgumentException("Period " + period + " must be positive");
+            }
             return new Builder(start, null, period);
         }