am 5d3a382a: am 6d7af4b7: Merge "DO NOT MERGE Allow a custom component to handle network policy notifications" into cw-e-dev

* commit '5d3a382aa4c7d93df15aaa2cd9b1144c7c8d2d1b':
  DO NOT MERGE Allow a custom component to handle network policy notifications
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index 7f5f377..9e639e8 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -48,6 +48,9 @@
     /** Snooze limit on policy matching given template. */
     void snoozeLimit(in NetworkTemplate template);
 
+    /** Snooze warning on policy matching given template. */
+    void snoozeWarning(in NetworkTemplate template);
+
     /** Control if background data is restricted system-wide. */
     void setRestrictBackground(boolean restrictBackground);
     boolean getRestrictBackground();
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 3f40484..02dae8a 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -20,6 +20,7 @@
 import static android.net.NetworkPolicy.CYCLE_NONE;
 import static android.text.format.Time.MONTH_DAY;
 
+import android.annotation.SystemApi;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -80,6 +81,54 @@
      */
     public static final String EXTRA_NETWORK_TEMPLATE = "android.net.NETWORK_TEMPLATE";
 
+    /**
+     * Broadcast intent action for informing a custom component about a network policy
+     * notification.
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_SHOW_NETWORK_POLICY_NOTIFICATION =
+            "android.net.action.SHOW_NETWORK_POLICY_NOTIFICATION";
+
+    /**
+     * The sequence number associated with the notification - a higher number
+     * indicates previous notifications may be disregarded.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_NOTIFICATION_SEQUENCE_NUMBER =
+            "android.net.extra.NOTIFICATION_SEQUENCE_NUMBER";
+
+    /**
+     * The type of notification that should be presented to the user.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_NOTIFICATION_TYPE = "android.net.extra.NOTIFICATION_TYPE";
+
+    @SystemApi
+    public static final int NOTIFICATION_TYPE_NONE = 0;
+    @SystemApi
+    public static final int NOTIFICATION_TYPE_USAGE_WARNING = 1;
+    @SystemApi
+    public static final int NOTIFICATION_TYPE_USAGE_REACHED_LIMIT = 2;
+    @SystemApi
+    public static final int NOTIFICATION_TYPE_USAGE_EXCEEDED_LIMIT = 3;
+
+    /**
+     * The number of bytes used on the network in the notification.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_BYTES_USED = "android.net.extra.BYTES_USED";
+
+    /**
+     * The network policy for the network in the notification.
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_NETWORK_POLICY = "android.net.extra.NETWORK_POLICY";
+
     private final Context mContext;
     private INetworkPolicyManager mService;
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 629d14b..79ba355 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -320,6 +320,8 @@
     <protected-broadcast android:name="android.internal.policy.action.BURN_IN_PROTECTION" />
     <protected-broadcast android:name="android.app.action.SYSTEM_UPDATE_POLICY_CHANGED" />
     <protected-broadcast android:name="android.app.action.DEVICE_OWNER_CHANGED" />
+
+    <protected-broadcast android:name="android.net.action.SHOW_NETWORK_POLICY_NOTIFICATION" />
     <!-- ====================================================================== -->
     <!--                          RUNTIME PERMISSIONS                           -->
     <!-- ====================================================================== -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 8e88886..678db54 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2275,4 +2275,8 @@
     <!-- The OEM specified sensor string type for the gesture to launch camera app, this value
          must match the value of config_cameraLaunchGestureSensorType in OEM's HAL -->
     <string translatable="false" name="config_cameraLaunchGestureSensorStringType"></string>
+
+    <!-- Name of the component to handle network policy notifications. If present,
+         disables NetworkPolicyManagerService's presentation of data-usage notifications. -->
+    <string translatable="false" name="config_networkPolicyNotificationComponent"></string>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index dae5db0..1cd5248 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2321,4 +2321,6 @@
   <!-- Gesture -->
   <java-symbol type="integer" name="config_cameraLaunchGestureSensorType" />
   <java-symbol type="string" name="config_cameraLaunchGestureSensorStringType" />
+
+  <java-symbol type="string" name="config_networkPolicyNotificationComponent" />
 </resources>
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index c0d0d13..2f5438e 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -108,6 +108,7 @@
 import android.net.NetworkIdentity;
 import android.net.NetworkInfo;
 import android.net.NetworkPolicy;
+import android.net.NetworkPolicyManager;
 import android.net.NetworkQuotaInfo;
 import android.net.NetworkState;
 import android.net.NetworkTemplate;
@@ -201,6 +202,8 @@
     private static final int VERSION_LATEST = VERSION_SWITCH_UID;
 
     @VisibleForTesting
+    public static final int TYPE_NONE = 0;
+    @VisibleForTesting
     public static final int TYPE_WARNING = 0x1;
     @VisibleForTesting
     public static final int TYPE_LIMIT = 0x2;
@@ -260,6 +263,9 @@
     private PowerManagerInternal mPowerManagerInternal;
     private IDeviceIdleController mDeviceIdleController;
 
+    private final ComponentName mNotificationComponent;
+    private int mNotificationSequenceNumber;
+
     final Object mRulesLock = new Object();
 
     volatile boolean mSystemReady;
@@ -357,6 +363,11 @@
         mPolicyFile = new AtomicFile(new File(systemDir, "netpolicy.xml"));
 
         mAppOps = context.getSystemService(AppOpsManager.class);
+
+        final String notificationComponent = context.getString(
+                R.string.config_networkPolicyNotificationComponent);
+        mNotificationComponent = notificationComponent != null
+                ? ComponentName.unflattenFromString(notificationComponent) : null;
     }
 
     public void bindConnectivityManager(IConnectivityManager connManager) {
@@ -778,6 +789,11 @@
         final ArraySet<String> beforeNotifs = new ArraySet<String>(mActiveNotifs);
         mActiveNotifs.clear();
 
+        // increment the sequence number so custom components know
+        // this update is new
+        mNotificationSequenceNumber++;
+        boolean hasNotifications = false;
+
         // TODO: when switching to kernel notifications, compute next future
         // cycle boundary to recompute notifications.
 
@@ -794,6 +810,7 @@
             final long totalBytes = getTotalBytes(policy.template, start, end);
 
             if (policy.isOverLimit(totalBytes)) {
+                hasNotifications = true;
                 if (policy.lastLimitSnooze >= start) {
                     enqueueNotification(policy, TYPE_LIMIT_SNOOZED, totalBytes);
                 } else {
@@ -806,10 +823,18 @@
 
                 if (policy.isOverWarning(totalBytes) && policy.lastWarningSnooze < start) {
                     enqueueNotification(policy, TYPE_WARNING, totalBytes);
+                    hasNotifications = true;
                 }
             }
         }
 
+        // right now we don't care about restricted background notifications
+        // in the custom notification component, so trigger an update now
+        // if we didn't update anything this pass
+        if (!hasNotifications) {
+            sendNotificationToCustomComponent(null, TYPE_NONE, 0);
+        }
+
         // ongoing notification when restricting background data
         if (mRestrictBackground) {
             enqueueRestrictedNotification(TAG_ALLOW_BACKGROUND);
@@ -856,6 +881,12 @@
      * {@link NetworkPolicy#limitBytes}, potentially showing dialog to user.
      */
     private void notifyOverLimitLocked(NetworkTemplate template) {
+        if (mNotificationComponent != null) {
+            // It is the job of the notification component to handle UI,
+            // so we do nothing here
+            return;
+        }
+
         if (!mOverLimitNotified.contains(template)) {
             mContext.startActivity(buildNetworkOverLimitIntent(template));
             mOverLimitNotified.add(template);
@@ -874,11 +905,55 @@
         return TAG + ":" + policy.template.hashCode() + ":" + type;
     }
 
+    private boolean sendNotificationToCustomComponent(
+            NetworkPolicy policy,
+            int type,
+            long totalBytes) {
+        if (mNotificationComponent == null) {
+            return false;
+        }
+
+        Intent intent = new Intent();
+        intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        intent.setComponent(mNotificationComponent);
+
+        int notificationType = NetworkPolicyManager.NOTIFICATION_TYPE_NONE;
+        switch (type) {
+            case TYPE_WARNING:
+                notificationType = NetworkPolicyManager.NOTIFICATION_TYPE_USAGE_WARNING;
+                break;
+            case TYPE_LIMIT:
+                notificationType = NetworkPolicyManager.NOTIFICATION_TYPE_USAGE_REACHED_LIMIT;
+                break;
+            case TYPE_LIMIT_SNOOZED:
+                notificationType = NetworkPolicyManager.NOTIFICATION_TYPE_USAGE_EXCEEDED_LIMIT;
+                break;
+        }
+
+        intent.setAction(NetworkPolicyManager.ACTION_SHOW_NETWORK_POLICY_NOTIFICATION);
+        intent.putExtra(NetworkPolicyManager.EXTRA_NOTIFICATION_TYPE, notificationType);
+        intent.putExtra(
+                NetworkPolicyManager.EXTRA_NOTIFICATION_SEQUENCE_NUMBER,
+                mNotificationSequenceNumber);
+
+        if (notificationType != NetworkPolicyManager.NOTIFICATION_TYPE_NONE) {
+            intent.putExtra(NetworkPolicyManager.EXTRA_NETWORK_POLICY, policy);
+            intent.putExtra(NetworkPolicyManager.EXTRA_BYTES_USED, totalBytes);
+        }
+
+        mContext.sendBroadcast(intent);
+        return true;
+    }
+
     /**
      * Show notification for combined {@link NetworkPolicy} and specific type,
      * like {@link #TYPE_LIMIT}. Okay to call multiple times.
      */
     private void enqueueNotification(NetworkPolicy policy, int type, long totalBytes) {
+        if (sendNotificationToCustomComponent(policy, type, totalBytes)) {
+            return;
+        }
+
         final String tag = buildNotificationTag(policy, type);
         final Notification.Builder builder = new Notification.Builder(mContext);
         builder.setOnlyAlertOnce(true);
@@ -1738,6 +1813,19 @@
         }
     }
 
+    @Override
+    public void snoozeWarning(NetworkTemplate template) {
+        mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            // TODO: this seems like a race condition? (along with snoozeLimit above)
+            performSnooze(template, TYPE_WARNING);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
     void performSnooze(NetworkTemplate template, int type) {
         maybeRefreshTrustedTime();
         final long currentTime = currentTimeMillis();