Merge "Zen: New user flow for requesting DND access." into mnc-dev
diff --git a/Android.mk b/Android.mk
index 4d8a37c..6bfed8e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -74,7 +74,6 @@
 	core/java/android/app/IBackupAgent.aidl \
 	core/java/android/app/IInstrumentationWatcher.aidl \
 	core/java/android/app/INotificationManager.aidl \
-	core/java/android/app/INotificationManagerCallback.aidl \
 	core/java/android/app/IProcessObserver.aidl \
 	core/java/android/app/ISearchManager.aidl \
 	core/java/android/app/ISearchManagerCallback.aidl \
diff --git a/api/current.txt b/api/current.txt
index 5bea5647..2e37960 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -12,6 +12,7 @@
     field public static final java.lang.String ACCESS_LOCATION_EXTRA_COMMANDS = "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS";
     field public static final java.lang.String ACCESS_MOCK_LOCATION = "android.permission.ACCESS_MOCK_LOCATION";
     field public static final java.lang.String ACCESS_NETWORK_STATE = "android.permission.ACCESS_NETWORK_STATE";
+    field public static final java.lang.String ACCESS_NOTIFICATION_POLICY = "android.permission.ACCESS_NOTIFICATION_POLICY";
     field public static final java.lang.String ACCESS_SURFACE_FLINGER = "android.permission.ACCESS_SURFACE_FLINGER";
     field public static final java.lang.String ACCESS_WIFI_STATE = "android.permission.ACCESS_WIFI_STATE";
     field public static final java.lang.String ACCOUNT_MANAGER = "android.permission.ACCOUNT_MANAGER";
@@ -5117,10 +5118,10 @@
     method public boolean isNotificationPolicyAccessGranted();
     method public void notify(int, android.app.Notification);
     method public void notify(java.lang.String, int, android.app.Notification);
-    method public void requestPolicyAccess(android.app.NotificationManager.NotificationPolicyAccessRequestCallback, android.os.Handler);
     method public final void setInterruptionFilter(int);
     method public void setNotificationPolicy(android.app.NotificationManager.Policy);
     field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED";
+    field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED";
     field public static final java.lang.String ACTION_NOTIFICATION_POLICY_CHANGED = "android.app.action.NOTIFICATION_POLICY_CHANGED";
     field public static final int INTERRUPTION_FILTER_ALARMS = 4; // 0x4
     field public static final int INTERRUPTION_FILTER_ALL = 1; // 0x1
@@ -5129,12 +5130,6 @@
     field public static final int INTERRUPTION_FILTER_UNKNOWN = 0; // 0x0
   }
 
-  public static abstract class NotificationManager.NotificationPolicyAccessRequestCallback {
-    ctor public NotificationManager.NotificationPolicyAccessRequestCallback();
-    method public abstract void onAccessDenied();
-    method public abstract void onAccessGranted();
-  }
-
   public static class NotificationManager.Policy implements android.os.Parcelable {
     ctor public NotificationManager.Policy(int, int, int);
     method public int describeContents();
@@ -26548,6 +26543,7 @@
     field public static final java.lang.String ACTION_NFC_PAYMENT_SETTINGS = "android.settings.NFC_PAYMENT_SETTINGS";
     field public static final java.lang.String ACTION_NFC_SETTINGS = "android.settings.NFC_SETTINGS";
     field public static final java.lang.String ACTION_NOTIFICATION_LISTENER_SETTINGS = "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS";
+    field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS = "android.settings.NOTIFICATION_POLICY_ACCESS_SETTINGS";
     field public static final java.lang.String ACTION_PRINT_SETTINGS = "android.settings.ACTION_PRINT_SETTINGS";
     field public static final java.lang.String ACTION_PRIVACY_SETTINGS = "android.settings.PRIVACY_SETTINGS";
     field public static final java.lang.String ACTION_QUICK_LAUNCH_SETTINGS = "android.settings.QUICK_LAUNCH_SETTINGS";
@@ -26566,7 +26562,6 @@
     field public static final java.lang.String ACTION_WIFI_IP_SETTINGS = "android.settings.WIFI_IP_SETTINGS";
     field public static final java.lang.String ACTION_WIFI_SETTINGS = "android.settings.WIFI_SETTINGS";
     field public static final java.lang.String ACTION_WIRELESS_SETTINGS = "android.settings.WIRELESS_SETTINGS";
-    field public static final java.lang.String ACTION_ZEN_ACCESS_SETTINGS = "android.settings.ZEN_ACCESS_SETTINGS";
     field public static final java.lang.String AUTHORITY = "settings";
     field public static final java.lang.String EXTRA_ACCOUNT_TYPES = "account_types";
     field public static final java.lang.String EXTRA_AIRPLANE_MODE_ENABLED = "airplane_mode_enabled";
diff --git a/api/system-current.txt b/api/system-current.txt
index d67ffe1..7d3da37 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -18,6 +18,7 @@
     field public static final java.lang.String ACCESS_NETWORK_CONDITIONS = "android.permission.ACCESS_NETWORK_CONDITIONS";
     field public static final java.lang.String ACCESS_NETWORK_STATE = "android.permission.ACCESS_NETWORK_STATE";
     field public static final java.lang.String ACCESS_NOTIFICATIONS = "android.permission.ACCESS_NOTIFICATIONS";
+    field public static final java.lang.String ACCESS_NOTIFICATION_POLICY = "android.permission.ACCESS_NOTIFICATION_POLICY";
     field public static final java.lang.String ACCESS_SURFACE_FLINGER = "android.permission.ACCESS_SURFACE_FLINGER";
     field public static final java.lang.String ACCESS_WIFI_STATE = "android.permission.ACCESS_WIFI_STATE";
     field public static final java.lang.String ACCOUNT_MANAGER = "android.permission.ACCOUNT_MANAGER";
@@ -5213,10 +5214,10 @@
     method public boolean isNotificationPolicyAccessGranted();
     method public void notify(int, android.app.Notification);
     method public void notify(java.lang.String, int, android.app.Notification);
-    method public void requestPolicyAccess(android.app.NotificationManager.NotificationPolicyAccessRequestCallback, android.os.Handler);
     method public final void setInterruptionFilter(int);
     method public void setNotificationPolicy(android.app.NotificationManager.Policy);
     field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED";
+    field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED";
     field public static final java.lang.String ACTION_NOTIFICATION_POLICY_CHANGED = "android.app.action.NOTIFICATION_POLICY_CHANGED";
     field public static final int INTERRUPTION_FILTER_ALARMS = 4; // 0x4
     field public static final int INTERRUPTION_FILTER_ALL = 1; // 0x1
@@ -5225,12 +5226,6 @@
     field public static final int INTERRUPTION_FILTER_UNKNOWN = 0; // 0x0
   }
 
-  public static abstract class NotificationManager.NotificationPolicyAccessRequestCallback {
-    ctor public NotificationManager.NotificationPolicyAccessRequestCallback();
-    method public abstract void onAccessDenied();
-    method public abstract void onAccessGranted();
-  }
-
   public static class NotificationManager.Policy implements android.os.Parcelable {
     ctor public NotificationManager.Policy(int, int, int);
     method public int describeContents();
@@ -28580,6 +28575,7 @@
     field public static final java.lang.String ACTION_NFC_PAYMENT_SETTINGS = "android.settings.NFC_PAYMENT_SETTINGS";
     field public static final java.lang.String ACTION_NFC_SETTINGS = "android.settings.NFC_SETTINGS";
     field public static final java.lang.String ACTION_NOTIFICATION_LISTENER_SETTINGS = "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS";
+    field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS = "android.settings.NOTIFICATION_POLICY_ACCESS_SETTINGS";
     field public static final java.lang.String ACTION_PRINT_SETTINGS = "android.settings.ACTION_PRINT_SETTINGS";
     field public static final java.lang.String ACTION_PRIVACY_SETTINGS = "android.settings.PRIVACY_SETTINGS";
     field public static final java.lang.String ACTION_QUICK_LAUNCH_SETTINGS = "android.settings.QUICK_LAUNCH_SETTINGS";
@@ -28598,7 +28594,6 @@
     field public static final java.lang.String ACTION_WIFI_IP_SETTINGS = "android.settings.WIFI_IP_SETTINGS";
     field public static final java.lang.String ACTION_WIFI_SETTINGS = "android.settings.WIFI_SETTINGS";
     field public static final java.lang.String ACTION_WIRELESS_SETTINGS = "android.settings.WIRELESS_SETTINGS";
-    field public static final java.lang.String ACTION_ZEN_ACCESS_SETTINGS = "android.settings.ZEN_ACCESS_SETTINGS";
     field public static final java.lang.String AUTHORITY = "settings";
     field public static final java.lang.String EXTRA_ACCOUNT_TYPES = "account_types";
     field public static final java.lang.String EXTRA_AIRPLANE_MODE_ENABLED = "airplane_mode_enabled";
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 63ff005..f78fb47 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -17,7 +17,6 @@
 
 package android.app;
 
-import android.app.INotificationManagerCallback;
 import android.app.ITransientNotification;
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -87,7 +86,6 @@
     oneway void setZenMode(int mode, in Uri conditionId, String reason);
     oneway void notifyConditions(String pkg, in IConditionProvider provider, in Condition[] conditions);
     oneway void requestZenModeConditions(in IConditionListener callback, int relevance);
-    oneway void requestNotificationPolicyAccess(String pkg, in INotificationManagerCallback callback);
     boolean isNotificationPolicyAccessGranted(String pkg);
     NotificationManager.Policy getNotificationPolicy(String pkg);
     void setNotificationPolicy(String pkg, in NotificationManager.Policy policy);
diff --git a/core/java/android/app/INotificationManagerCallback.aidl b/core/java/android/app/INotificationManagerCallback.aidl
deleted file mode 100644
index 9929745..0000000
--- a/core/java/android/app/INotificationManagerCallback.aidl
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
- * Copyright (c) 2015, 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.app;
-
-import android.app.NotificationManager;
-
-/** @hide */
-oneway interface INotificationManagerCallback {
-    void onPolicyRequestResult(boolean granted);
-}
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 557964b..0904e21 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -101,6 +101,16 @@
             = "android.os.action.ACTION_EFFECTS_SUPPRESSOR_CHANGED";
 
     /**
+     * Intent that is broadcast when the state of {@link #isNotificationPolicyAccessGranted()}
+     * changes.
+     *
+     * This broadcast is only sent to registered receivers, and only to the apps that have changed.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED
+            = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED";
+
+    /**
      * Intent that is broadcast when the state of getNotificationPolicy() changes.
      * This broadcast is only sent to registered receivers.
      */
@@ -403,55 +413,18 @@
     }
 
     /**
-     * Requests the ability to read/modify notification policy for the calling package.
-     *
-     * @param callback required, used to receive the granted or the denied signal.
-     * @param handler The handler used when receiving the result.
-     *                If null, the current thread is used.
-     */
-    public void requestPolicyAccess(@NonNull final NotificationPolicyAccessRequestCallback callback,
-            @Nullable Handler handler) {
-        checkRequired("callback", callback);
-        final Handler h = handler != null ? handler : new Handler();
-        INotificationManager service = getService();
-        try {
-            service.requestNotificationPolicyAccess(mContext.getOpPackageName(),
-                    new INotificationManagerCallback.Stub() {
-                @Override
-                public void onPolicyRequestResult(final boolean granted) throws RemoteException {
-                    h.post(new Runnable() {
-                        @Override
-                        public void run() {
-                            if (granted) {
-                                callback.onAccessGranted();
-                            } else {
-                                callback.onAccessDenied();
-                            }
-                        }
-                    });
-                }
-            });
-        } catch (RemoteException e) {
-        }
-    }
-
-    /** Callback for receiving the result of a policy access request. */
-    public static abstract class NotificationPolicyAccessRequestCallback {
-        /**
-         * Received if the request was granted for this package.
-         */
-        public abstract void onAccessGranted();
-
-        /**
-         * Received if the request was denied for this package.
-         */
-        public abstract void onAccessDenied();
-    }
-
-    /**
      * Checks the ability to read/modify notification policy for the calling package.
      *
+     * <p>
      * Returns true if the calling package can read/modify notification policy.
+     *
+     * <p>
+     * Request policy access by sending the user to the activity that matches the system intent
+     * action {@link android.provider.Settings#ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS}.
+     *
+     * <p>
+     * Use {@link #ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED} to listen for
+     * user grant or denial of this access.
      */
     public boolean isNotificationPolicyAccessGranted() {
         INotificationManager service = getService();
@@ -476,7 +449,8 @@
      * Gets the current notification policy.
      *
      * <p>
-     * Only available if policy access is granted.
+     * Only available if policy access is granted to this package.
+     * See {@link #isNotificationPolicyAccessGranted}.
      */
     public Policy getNotificationPolicy() {
         INotificationManager service = getService();
@@ -491,7 +465,8 @@
      * Sets the current notification policy.
      *
      * <p>
-     * Only available if policy access is granted.
+     * Only available if policy access is granted to this package.
+     * See {@link #isNotificationPolicyAccessGranted}.
      *
      * @param policy The new desired policy.
      */
@@ -716,7 +691,8 @@
      * unavailable.
      *
      * <p>
-     * Only available if policy access is granted.
+     * Only available if policy access is granted to this package.
+     * See {@link #isNotificationPolicyAccessGranted}.
      */
     public final int getCurrentInterruptionFilter() {
         final INotificationManager service = getService();
@@ -738,7 +714,8 @@
      * unavailable.
      *
      * <p>
-     * Only available if policy access is granted.
+     * Only available if policy access is granted to this package.
+     * See {@link #isNotificationPolicyAccessGranted}.
      */
     public final void setInterruptionFilter(int interruptionFilter) {
         final INotificationManager service = getService();
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 37645b5..cac4a53 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -811,14 +811,17 @@
     /**
      * Activity Action: Show Do Not Disturb access settings.
      * <p>
-     * In some cases, a matching Activity may not exist, so ensure you safeguard against this.
+     * Users can grant and deny access to Do Not Disturb configuration from here.
+     * See {@link android.app.NotificationManager#isNotificationPolicyAccessGranted()} for more
+     * details.
      * <p>
      * Input: Nothing.
      * <p>
      * Output: Nothing.
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public static final String ACTION_ZEN_ACCESS_SETTINGS = "android.settings.ZEN_ACCESS_SETTINGS";
+    public static final String ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS
+            = "android.settings.NOTIFICATION_POLICY_ACCESS_SETTINGS";
 
     /**
      * @hide
@@ -5425,7 +5428,7 @@
         public static final String ASSIST_STRUCTURE_ENABLED = "assist_structure_enabled";
 
         /**
-         * Names of the packages that the current user has explicitly allowed to
+         * Names of the service components that the current user has explicitly allowed to
          * see all of the user's notifications, separated by ':'.
          *
          * @hide
@@ -5433,6 +5436,15 @@
         public static final String ENABLED_NOTIFICATION_LISTENERS = "enabled_notification_listeners";
 
         /**
+         * Names of the packages that the current user has explicitly allowed to
+         * manage notification policy configuration, separated by ':'.
+         *
+         * @hide
+         */
+        public static final String ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES =
+                "enabled_notification_policy_access_packages";
+
+        /**
          * @hide
          */
         public static final String ENABLED_CONDITION_PROVIDERS = "enabled_condition_providers";
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 8accc0a..f32918d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2326,6 +2326,12 @@
     <permission android:name="android.permission.ACCESS_NOTIFICATIONS"
         android:protectionLevel="signature|system" />
 
+    <!-- Marker permission for applications that wish to access notification policy. -->
+    <permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY"
+        android:description="@string/permdesc_access_notification_policy"
+        android:label="@string/permlab_access_notification_policy"
+        android:protectionLevel="normal" />
+
     <!-- Allows access to keyguard secure storage.  Only allowed for system processes.
         @hide -->
     <permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE"
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index a2f8918..3f828e7 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1435,6 +1435,11 @@
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_bindCarrierServices">Allows the holder to bind to carrier services. Should never be needed for normal apps.</string>
 
+    <!-- Title of an application permission, for applications that wish to access notification policy. -->
+    <string name="permlab_access_notification_policy">access Do Not Disturb</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_access_notification_policy">Allows the app to read and write Do Not Disturb configuration.</string>
+
     <!-- Policy administration -->
 
     <!-- Title of policy access to limiting the user's password choices -->
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 72383a8..8ee2076 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -29,7 +29,6 @@
 import android.app.AppOpsManager;
 import android.app.IActivityManager;
 import android.app.INotificationManager;
-import android.app.INotificationManagerCallback;
 import android.app.ITransientNotification;
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -130,6 +129,7 @@
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map.Entry;
 import java.util.Objects;
 
@@ -237,8 +237,7 @@
             new ArrayMap<String, NotificationRecord>();
     final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
     final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
-    private final ArrayMap<String, Boolean> mPolicyAccess = new ArrayMap<>();
-
+    final PolicyAccess mPolicyAccess = new PolicyAccess();
 
     // The last key in this list owns the hardware.
     ArrayList<String> mLights = new ArrayList<>();
@@ -781,7 +780,7 @@
         }
     };
 
-    class SettingsObserver extends ContentObserver {
+    private final class SettingsObserver extends ContentObserver {
         private final Uri NOTIFICATION_LIGHT_PULSE_URI
                 = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
 
@@ -1635,7 +1634,7 @@
         }
 
         private boolean checkPackagePolicyAccess(String pkg) {
-            return Boolean.TRUE.equals(mPolicyAccess.get(pkg));
+            return mPolicyAccess.isPackageGranted(pkg);
         }
 
         private boolean checkPolicyAccess(String pkg) {
@@ -1718,31 +1717,6 @@
         }
 
         @Override
-        public void requestNotificationPolicyAccess(String pkg,
-                INotificationManagerCallback callback) throws RemoteException {
-            if (callback == null) {
-                Slog.w(TAG, "requestNotificationPolicyAccess: no callback specified");
-                return;
-            }
-            if (pkg == null) {
-                Slog.w(TAG, "requestNotificationPolicyAccess denied: no package specified");
-                callback.onPolicyRequestResult(false);
-                return;
-            }
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                synchronized (mNotificationList) {
-                    // immediately grant for now
-                    mPolicyAccess.put(pkg, true);
-                    if (DBG) Slog.w(TAG, "requestNotificationPolicyAccess granted for " + pkg);
-                }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-            callback.onPolicyRequestResult(true);
-        }
-
-        @Override
         public boolean isNotificationPolicyAccessGranted(String pkg) {
             return checkPolicyAccess(pkg);
         }
@@ -1759,13 +1733,7 @@
             enforceSystemOrSystemUI("request policy access packages");
             final long identity = Binder.clearCallingIdentity();
             try {
-                synchronized (mNotificationList) {
-                    final String[] rt = new String[mPolicyAccess.size()];
-                    for (int i = 0; i < mPolicyAccess.size(); i++) {
-                        rt[i] = mPolicyAccess.keyAt(i);
-                    }
-                    return rt;
-                }
+                return mPolicyAccess.getRequestingPackages();
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -3517,4 +3485,73 @@
             return value;
         }
     }
+
+    private final class PolicyAccess {
+        private static final String SEPARATOR = ":";
+        private final String[] PERM = {
+            android.Manifest.permission.ACCESS_NOTIFICATION_POLICY
+        };
+
+        public boolean isPackageGranted(String pkg) {
+            return pkg != null && getGrantedPackages().contains(pkg);
+        }
+
+        public void put(String pkg, boolean granted) {
+            if (pkg == null) return;
+            final ArraySet<String> pkgs = getGrantedPackages();
+            boolean changed;
+            if (granted) {
+                changed = pkgs.add(pkg);
+            } else {
+                changed = pkgs.remove(pkg);
+            }
+            if (!changed) return;
+            final String setting = TextUtils.join(SEPARATOR, pkgs);
+            final int currentUser = ActivityManager.getCurrentUser();
+            Settings.Secure.putStringForUser(getContext().getContentResolver(),
+                    Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
+                    setting,
+                    currentUser);
+            getContext().sendBroadcastAsUser(new Intent(NotificationManager
+                    .ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
+                .setPackage(pkg)
+                .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), new UserHandle(currentUser), null);
+        }
+
+        public ArraySet<String> getGrantedPackages() {
+            final ArraySet<String> pkgs = new ArraySet<>();
+            final String setting = Settings.Secure.getStringForUser(
+                    getContext().getContentResolver(),
+                    Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
+                    ActivityManager.getCurrentUser());
+            if (setting != null) {
+                final String[] tokens = setting.split(SEPARATOR);
+                for (int i = 0; i < tokens.length; i++) {
+                    String token = tokens[i];
+                    if (token != null) {
+                        token.trim();
+                    }
+                    if (TextUtils.isEmpty(token)) {
+                        continue;
+                    }
+                    pkgs.add(token);
+                }
+            }
+            return pkgs;
+        }
+
+        public String[] getRequestingPackages() throws RemoteException {
+            final ParceledListSlice list = AppGlobals.getPackageManager()
+                    .getPackagesHoldingPermissions(PERM, 0 /*flags*/,
+                            ActivityManager.getCurrentUser());
+            final List<PackageInfo> pkgs = list.getList();
+            if (pkgs == null || pkgs.isEmpty()) return new String[0];
+            final int N = pkgs.size();
+            final String[] rt = new String[N];
+            for (int i = 0; i < N; i++) {
+                rt[i] = pkgs.get(i).packageName;
+            }
+            return rt;
+        }
+    }
 }