Notify apps when channels/groups are blocked/unblocked

So that apps can disable/enable receivers/jobs/etc. that
would only need to run to post notifications to the blocked
channel(s).

Additionally let apps retrieve an individual group so they
can inspect blocked state.

Change-Id: I733b70c62cd0482d0cf9692ea9b00cf313ad7b81
Fixes: 36530302
Test: runtest systemui-notification, cts
diff --git a/api/current.txt b/api/current.txt
index c6d5430..02aba41 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5616,6 +5616,7 @@
     method public final int getCurrentInterruptionFilter();
     method public int getImportance();
     method public android.app.NotificationChannel getNotificationChannel(java.lang.String);
+    method public android.app.NotificationChannelGroup getNotificationChannelGroup(java.lang.String);
     method public java.util.List<android.app.NotificationChannelGroup> getNotificationChannelGroups();
     method public java.util.List<android.app.NotificationChannel> getNotificationChannels();
     method public android.app.NotificationManager.Policy getNotificationPolicy();
@@ -5628,8 +5629,12 @@
     method public void setNotificationPolicy(android.app.NotificationManager.Policy);
     method public boolean updateAutomaticZenRule(java.lang.String, android.app.AutomaticZenRule);
     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_CHANNEL_BLOCK_STATE_CHANGED = "android.app.action.NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED";
+    field public static final java.lang.String ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED = "android.app.action.NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_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 java.lang.String EXTRA_BLOCKED_STATE = "android.app.extra.BLOCKED_STATE";
+    field public static final java.lang.String EXTRA_BLOCK_STATE_CHANGED_ID = "android.app.extra.BLOCK_STATE_CHANGED_ID";
     field public static final int IMPORTANCE_DEFAULT = 3; // 0x3
     field public static final int IMPORTANCE_HIGH = 4; // 0x4
     field public static final int IMPORTANCE_LOW = 2; // 0x2
diff --git a/api/system-current.txt b/api/system-current.txt
index 94e3786..a29d401 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5825,6 +5825,7 @@
     method public final int getCurrentInterruptionFilter();
     method public int getImportance();
     method public android.app.NotificationChannel getNotificationChannel(java.lang.String);
+    method public android.app.NotificationChannelGroup getNotificationChannelGroup(java.lang.String);
     method public java.util.List<android.app.NotificationChannelGroup> getNotificationChannelGroups();
     method public java.util.List<android.app.NotificationChannel> getNotificationChannels();
     method public android.app.NotificationManager.Policy getNotificationPolicy();
@@ -5837,8 +5838,12 @@
     method public void setNotificationPolicy(android.app.NotificationManager.Policy);
     method public boolean updateAutomaticZenRule(java.lang.String, android.app.AutomaticZenRule);
     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_CHANNEL_BLOCK_STATE_CHANGED = "android.app.action.NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED";
+    field public static final java.lang.String ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED = "android.app.action.NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_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 java.lang.String EXTRA_BLOCKED_STATE = "android.app.extra.BLOCKED_STATE";
+    field public static final java.lang.String EXTRA_BLOCK_STATE_CHANGED_ID = "android.app.extra.BLOCK_STATE_CHANGED_ID";
     field public static final int IMPORTANCE_DEFAULT = 3; // 0x3
     field public static final int IMPORTANCE_HIGH = 4; // 0x4
     field public static final int IMPORTANCE_LOW = 2; // 0x2
@@ -48173,8 +48178,8 @@
   }
 
   public final class StatsManager {
-    method public byte[] getData(java.lang.String);
     method public boolean addConfiguration(java.lang.String, byte[], java.lang.String, java.lang.String);
+    method public byte[] getData(java.lang.String);
     method public boolean removeConfiguration(java.lang.String);
   }
 
diff --git a/api/test-current.txt b/api/test-current.txt
index aebf380..f1419bd 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -5647,6 +5647,7 @@
     method public android.content.ComponentName getEffectsSuppressor();
     method public int getImportance();
     method public android.app.NotificationChannel getNotificationChannel(java.lang.String);
+    method public android.app.NotificationChannelGroup getNotificationChannelGroup(java.lang.String);
     method public java.util.List<android.app.NotificationChannelGroup> getNotificationChannelGroups();
     method public java.util.List<android.app.NotificationChannel> getNotificationChannels();
     method public android.app.NotificationManager.Policy getNotificationPolicy();
@@ -5659,8 +5660,12 @@
     method public void setNotificationPolicy(android.app.NotificationManager.Policy);
     method public boolean updateAutomaticZenRule(java.lang.String, android.app.AutomaticZenRule);
     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_CHANNEL_BLOCK_STATE_CHANGED = "android.app.action.NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED";
+    field public static final java.lang.String ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED = "android.app.action.NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_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 java.lang.String EXTRA_BLOCKED_STATE = "android.app.extra.BLOCKED_STATE";
+    field public static final java.lang.String EXTRA_BLOCK_STATE_CHANGED_ID = "android.app.extra.BLOCK_STATE_CHANGED_ID";
     field public static final int IMPORTANCE_DEFAULT = 3; // 0x3
     field public static final int IMPORTANCE_HIGH = 4; // 0x4
     field public static final int IMPORTANCE_LOW = 2; // 0x2
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 9e926bd..d1aacad 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -72,6 +72,7 @@
     int getNumNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted);
     int getDeletedChannelCount(String pkg, int uid);
     void deleteNotificationChannelGroup(String pkg, String channelGroupId);
+    NotificationChannelGroup getNotificationChannelGroup(String pkg, String channelGroupId);
     ParceledListSlice getNotificationChannelGroups(String pkg);
     boolean onlyHasDefaultChannel(String pkg, int uid);
 
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index f931589..659cf16 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -93,6 +93,58 @@
     private static boolean localLOGV = false;
 
     /**
+     * Intent that is broadcast when a {@link NotificationChannel} is blocked
+     * (when {@link NotificationChannel#getImportance()} is {@link #IMPORTANCE_NONE}) or unblocked
+     * (when {@link NotificationChannel#getImportance()} is anything other than
+     * {@link #IMPORTANCE_NONE}).
+     *
+     * This broadcast is only sent to the app that owns the channel that has changed.
+     *
+     * Input: nothing
+     * Output: {@link #EXTRA_BLOCK_STATE_CHANGED_ID}
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED =
+            "android.app.action.NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED";
+
+    /**
+     * Extra for {@link #ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED} or
+     * {@link #ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED} containing the id of the
+     * object which has a new blocked state.
+     *
+     * The value will be the {@link NotificationChannel#getId()} of the channel for
+     * {@link #ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED} and
+     * the {@link NotificationChannelGroup#getId()} of the group for
+     * {@link #ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED}.
+     */
+    public static final String EXTRA_BLOCK_STATE_CHANGED_ID =
+            "android.app.extra.BLOCK_STATE_CHANGED_ID";
+
+    /**
+     * Extra for {@link #ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED} or
+     * {@link #ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED} containing the new blocked
+     * state as a boolean.
+     *
+     * The value will be {@code true} if this channel or group is now blocked and {@code false} if
+     * this channel or group is now unblocked.
+     */
+    public static final String EXTRA_BLOCKED_STATE = "android.app.extra.BLOCKED_STATE";
+
+
+    /**
+     * Intent that is broadcast when a {@link NotificationChannelGroup} is
+     * {@link NotificationChannelGroup#isBlocked() blocked} or unblocked.
+     *
+     * This broadcast is only sent to the app that owns the channel group that has changed.
+     *
+     * Input: nothing
+     * Output: {@link #EXTRA_BLOCK_STATE_CHANGED_ID}
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED =
+            "android.app.action.NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED";
+
+    /**
      * Intent that is broadcast when the state of {@link #getEffectsSuppressor()} changes.
      * This broadcast is only sent to registered receivers.
      *
@@ -504,6 +556,20 @@
     }
 
     /**
+     * Returns the notification channel group settings for a given channel group id.
+     *
+     * The channel group must belong to your package, or null will be returned.
+     */
+    public NotificationChannelGroup getNotificationChannelGroup(String channelGroupId) {
+        INotificationManager service = getService();
+        try {
+            return service.getNotificationChannelGroup(mContext.getPackageName(), channelGroupId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Returns all notification channel groups belonging to the calling app.
      */
     public List<NotificationChannelGroup> getNotificationChannelGroups() {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 86103e4..ed5a7a9 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -485,7 +485,6 @@
     <protected-broadcast android:name="android.content.jobscheduler.JOB_DEADLINE_EXPIRED" />
     <protected-broadcast android:name="android.intent.action.ACTION_UNSOL_RESPONSE_OEM_HOOK_RAW" />
     <protected-broadcast android:name="android.net.conn.CONNECTIVITY_CHANGE_SUPL" />
-    <protected-broadcast android:name="android.os.action.ACTION_EFFECTS_SUPPRESSOR_CHANGED" />
     <protected-broadcast android:name="android.os.action.LIGHT_DEVICE_IDLE_MODE_CHANGED" />
     <protected-broadcast android:name="android.os.storage.action.VOLUME_STATE_CHANGED" />
     <protected-broadcast android:name="android.os.storage.action.DISK_SCANNED" />
@@ -504,6 +503,8 @@
     <protected-broadcast android:name="android.app.action.NOTIFICATION_POLICY_CHANGED" />
     <protected-broadcast android:name="android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED" />
     <protected-broadcast android:name="android.os.action.ACTION_EFFECTS_SUPPRESSOR_CHANGED" />
+    <protected-broadcast android:name="android.app.action.NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED" />
+    <protected-broadcast android:name="android.app.action.NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED" />
 
     <protected-broadcast android:name="android.permission.GET_APP_GRANTED_URI_PERMISSIONS" />
     <protected-broadcast android:name="android.permission.CLEAR_APP_GRANTED_URI_PERMISSIONS" />
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 6ba1d8d..08da568 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -16,15 +16,21 @@
 
 package com.android.server.notification;
 
+import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED;
+import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED;
 import static android.app.NotificationManager.IMPORTANCE_LOW;
 import static android.app.NotificationManager.IMPORTANCE_MIN;
 import static android.app.NotificationManager.IMPORTANCE_NONE;
-import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
 import static android.content.pm.PackageManager.FEATURE_LEANBACK;
 import static android.content.pm.PackageManager.FEATURE_TELEVISION;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.UserHandle.USER_NULL;
 import static android.service.notification.NotificationListenerService
+        .HINT_HOST_DISABLE_CALL_EFFECTS;
+import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
+import static android.service.notification.NotificationListenerService
+        .HINT_HOST_DISABLE_NOTIFICATION_EFFECTS;
+import static android.service.notification.NotificationListenerService
         .NOTIFICATION_CHANNEL_OR_GROUP_ADDED;
 import static android.service.notification.NotificationListenerService
         .NOTIFICATION_CHANNEL_OR_GROUP_DELETED;
@@ -32,12 +38,13 @@
         .NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
 import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
 import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL;
-import static android.service.notification.NotificationListenerService.REASON_CHANNEL_BANNED;
 import static android.service.notification.NotificationListenerService.REASON_CANCEL;
 import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
+import static android.service.notification.NotificationListenerService.REASON_CHANNEL_BANNED;
 import static android.service.notification.NotificationListenerService.REASON_CLICK;
 import static android.service.notification.NotificationListenerService.REASON_ERROR;
-import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
+import static android.service.notification.NotificationListenerService
+        .REASON_GROUP_SUMMARY_CANCELED;
 import static android.service.notification.NotificationListenerService.REASON_LISTENER_CANCEL;
 import static android.service.notification.NotificationListenerService.REASON_LISTENER_CANCEL_ALL;
 import static android.service.notification.NotificationListenerService.REASON_PACKAGE_BANNED;
@@ -48,14 +55,10 @@
 import static android.service.notification.NotificationListenerService.REASON_TIMEOUT;
 import static android.service.notification.NotificationListenerService.REASON_UNAUTOBUNDLED;
 import static android.service.notification.NotificationListenerService.REASON_USER_STOPPED;
-import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
-import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS;
-import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS;
 import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF;
 import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON;
 import static android.service.notification.NotificationListenerService.TRIM_FULL;
 import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
-
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 
@@ -68,17 +71,17 @@
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
 import android.app.AutomaticZenRule;
-import android.app.NotificationChannelGroup;
-import android.app.backup.BackupManager;
 import android.app.IActivityManager;
 import android.app.INotificationManager;
 import android.app.ITransientNotification;
 import android.app.Notification;
 import android.app.NotificationChannel;
-import android.app.NotificationManager.Policy;
+import android.app.NotificationChannelGroup;
 import android.app.NotificationManager;
+import android.app.NotificationManager.Policy;
 import android.app.PendingIntent;
 import android.app.StatusBarManager;
+import android.app.backup.BackupManager;
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStatsManagerInternal;
 import android.companion.ICompanionDeviceManager;
@@ -119,8 +122,8 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
-import android.os.Vibrator;
 import android.os.VibrationEffect;
+import android.os.Vibrator;
 import android.provider.Settings;
 import android.service.notification.Adjustment;
 import android.service.notification.Condition;
@@ -174,9 +177,9 @@
 import com.android.server.lights.Light;
 import com.android.server.lights.LightsManager;
 import com.android.server.notification.ManagedServices.ManagedServiceInfo;
+import com.android.server.notification.ManagedServices.UserProfiles;
 import com.android.server.policy.PhoneWindowManager;
 import com.android.server.statusbar.StatusBarManagerInternal;
-import com.android.server.notification.ManagedServices.UserProfiles;
 
 import libcore.io.IoUtils;
 
@@ -196,7 +199,6 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.PrintWriter;
-import java.net.URI;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
@@ -1520,7 +1522,11 @@
                 }
             }
         }
+        final NotificationChannel preUpdate =
+                mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), true);
+
         mRankingHelper.updateNotificationChannel(pkg, uid, channel, true);
+        maybeNotifyChannelOwner(pkg, uid, preUpdate, channel);
 
         if (!fromListener) {
             final NotificationChannel modifiedChannel =
@@ -1533,12 +1539,40 @@
         savePolicyFile();
     }
 
+    private void maybeNotifyChannelOwner(String pkg, int uid, NotificationChannel preUpdate,
+            NotificationChannel update) {
+        try {
+            if ((preUpdate.getImportance() == IMPORTANCE_NONE
+                    && update.getImportance() != IMPORTANCE_NONE)
+                    || (preUpdate.getImportance() != IMPORTANCE_NONE
+                    && update.getImportance() == IMPORTANCE_NONE)) {
+                getContext().sendBroadcastAsUser(
+                        new Intent(ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED)
+                                .putExtra(NotificationManager.EXTRA_BLOCK_STATE_CHANGED_ID,
+                                        update.getId())
+                                .putExtra(NotificationManager.EXTRA_BLOCKED_STATE,
+                                        update.getImportance() == IMPORTANCE_NONE)
+                                .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+                                .setPackage(pkg),
+                        UserHandle.of(UserHandle.getUserId(uid)), null);
+            }
+        } catch (SecurityException e) {
+            Slog.w(TAG, "Can't notify app about channel change", e);
+        }
+    }
+
     private void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group,
             boolean fromApp, boolean fromListener) {
         Preconditions.checkNotNull(group);
         Preconditions.checkNotNull(pkg);
+
+        final NotificationChannelGroup preUpdate =
+                mRankingHelper.getNotificationChannelGroup(group.getId(), pkg, uid);
         mRankingHelper.createNotificationChannelGroup(pkg, uid, group,
                 fromApp);
+        if (!fromApp) {
+            maybeNotifyChannelGroupOwner(pkg, uid, preUpdate, group);
+        }
         if (!fromListener) {
             mListeners.notifyNotificationChannelGroupChanged(pkg,
                     UserHandle.of(UserHandle.getCallingUserId()), group,
@@ -1546,6 +1580,25 @@
         }
     }
 
+    private void maybeNotifyChannelGroupOwner(String pkg, int uid,
+            NotificationChannelGroup preUpdate, NotificationChannelGroup update) {
+        try {
+            if (preUpdate.isBlocked() != update.isBlocked()) {
+                getContext().sendBroadcastAsUser(
+                        new Intent(ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED)
+                                .putExtra(NotificationManager.EXTRA_BLOCK_STATE_CHANGED_ID,
+                                        update.getId())
+                                .putExtra(NotificationManager.EXTRA_BLOCKED_STATE,
+                                        update.isBlocked())
+                                .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+                                .setPackage(pkg),
+                        UserHandle.of(UserHandle.getUserId(uid)), null);
+            }
+        } catch (SecurityException e) {
+            Slog.w(TAG, "Can't notify app about group change", e);
+        }
+    }
+
     private ArrayList<ComponentName> getSuppressors() {
         ArrayList<ComponentName> names = new ArrayList<ComponentName>();
         for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
@@ -1924,11 +1977,18 @@
         }
 
         @Override
+        public NotificationChannelGroup getNotificationChannelGroup(String pkg, String groupId) {
+            checkCallerIsSystemOrSameApp(pkg);
+            return mRankingHelper.getNotificationChannelGroupWithChannels(
+                    pkg, Binder.getCallingUid(), groupId, false);
+        }
+
+        @Override
         public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(
                 String pkg) {
             checkCallerIsSystemOrSameApp(pkg);
-            return new ParceledListSlice<>(new ArrayList(
-                    mRankingHelper.getNotificationChannelGroups(pkg, Binder.getCallingUid())));
+            return mRankingHelper.getNotificationChannelGroups(
+                    pkg, Binder.getCallingUid(), false, false);
         }
 
         @Override
@@ -1998,7 +2058,7 @@
         public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroupsForPackage(
                 String pkg, int uid, boolean includeDeleted) {
             checkCallerIsSystem();
-            return mRankingHelper.getNotificationChannelGroups(pkg, uid, includeDeleted);
+            return mRankingHelper.getNotificationChannelGroups(pkg, uid, includeDeleted, true);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index b9c0d90..b1b0bf2 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -36,7 +36,7 @@
     void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group,
             boolean fromTargetApp);
     ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
-            int uid, boolean includeDeleted);
+            int uid, boolean includeDeleted, boolean includeNonGrouped);
     void createNotificationChannel(String pkg, int uid, NotificationChannel channel,
             boolean fromTargetApp);
     void updateNotificationChannel(String pkg, int uid, NotificationChannel channel, boolean fromUser);
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index d566a45..c0dccb5 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -750,12 +750,15 @@
             int uid) {
         Preconditions.checkNotNull(pkg);
         Record r = getRecord(pkg, uid);
+        if (r == null) {
+            return null;
+        }
         return r.groups.get(groupId);
     }
 
     @Override
     public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
-            int uid, boolean includeDeleted) {
+            int uid, boolean includeDeleted, boolean includeNonGrouped) {
         Preconditions.checkNotNull(pkg);
         Map<String, NotificationChannelGroup> groups = new ArrayMap<>();
         Record r = getRecord(pkg, uid);
@@ -783,7 +786,7 @@
                 }
             }
         }
-        if (nonGrouped.getChannels().size() > 0) {
+        if (includeNonGrouped && nonGrouped.getChannels().size() > 0) {
             groups.put(null, nonGrouped);
         }
         return new ParceledListSlice<>(new ArrayList<>(groups.values()));
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
index c145e82..55ec133 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -16,8 +16,10 @@
 
 package com.android.server.notification;
 
+import static android.app.NotificationManager.EXTRA_BLOCKED_STATE;
 import static android.app.NotificationManager.IMPORTANCE_HIGH;
 import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_MAX;
 import static android.app.NotificationManager.IMPORTANCE_NONE;
 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
 import static android.content.pm.PackageManager.FEATURE_WATCH;
@@ -940,6 +942,120 @@
     }
 
     @Test
+    public void testUpdateChannelNotifyCreatorBlock() throws Exception {
+        mService.setRankingHelper(mRankingHelper);
+        when(mRankingHelper.getNotificationChannel(eq(PKG), anyInt(),
+                eq(mTestNotificationChannel.getId()), anyBoolean()))
+                .thenReturn(mTestNotificationChannel);
+
+        NotificationChannel updatedChannel =
+                new NotificationChannel(mTestNotificationChannel.getId(),
+                        mTestNotificationChannel.getName(), IMPORTANCE_NONE);
+
+        mBinderService.updateNotificationChannelForPackage(PKG, 0, updatedChannel);
+        ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+        verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));
+
+        assertEquals(NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED,
+                captor.getValue().getAction());
+        assertEquals(PKG, captor.getValue().getPackage());
+        assertEquals(mTestNotificationChannel.getId(), captor.getValue().getStringExtra(
+                        NotificationManager.EXTRA_BLOCK_STATE_CHANGED_ID));
+        assertTrue(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, false));
+    }
+
+    @Test
+    public void testUpdateChannelNotifyCreatorUnblock() throws Exception {
+        NotificationChannel existingChannel =
+                new NotificationChannel(mTestNotificationChannel.getId(),
+                        mTestNotificationChannel.getName(), IMPORTANCE_NONE);
+        mService.setRankingHelper(mRankingHelper);
+        when(mRankingHelper.getNotificationChannel(eq(PKG), anyInt(),
+                eq(mTestNotificationChannel.getId()), anyBoolean()))
+                .thenReturn(existingChannel);
+
+        mBinderService.updateNotificationChannelForPackage(PKG, 0, mTestNotificationChannel);
+        ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+        verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));
+
+        assertEquals(NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED,
+                captor.getValue().getAction());
+        assertEquals(PKG, captor.getValue().getPackage());
+        assertEquals(mTestNotificationChannel.getId(), captor.getValue().getStringExtra(
+                NotificationManager.EXTRA_BLOCK_STATE_CHANGED_ID));
+        assertFalse(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, false));
+    }
+
+    @Test
+    public void testUpdateChannelNoNotifyCreatorOtherChanges() throws Exception {
+        NotificationChannel existingChannel =
+                new NotificationChannel(mTestNotificationChannel.getId(),
+                        mTestNotificationChannel.getName(), IMPORTANCE_MAX);
+        mService.setRankingHelper(mRankingHelper);
+        when(mRankingHelper.getNotificationChannel(eq(PKG), anyInt(),
+                eq(mTestNotificationChannel.getId()), anyBoolean()))
+                .thenReturn(existingChannel);
+
+        mBinderService.updateNotificationChannelForPackage(PKG, 0, mTestNotificationChannel);
+        verify(mContext, never()).sendBroadcastAsUser(any(), any(), eq(null));
+    }
+
+    @Test
+    public void testUpdateGroupNotifyCreatorBlock() throws Exception {
+        NotificationChannelGroup existing = new NotificationChannelGroup("id", "name");
+        mService.setRankingHelper(mRankingHelper);
+        when(mRankingHelper.getNotificationChannelGroup(eq(existing.getId()), eq(PKG), anyInt()))
+                .thenReturn(existing);
+
+        NotificationChannelGroup updated = new NotificationChannelGroup("id", "name");
+        updated.setBlocked(true);
+
+        mBinderService.updateNotificationChannelGroupForPackage(PKG, 0, updated);
+        ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+        verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));
+
+        assertEquals(NotificationManager.ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED,
+                captor.getValue().getAction());
+        assertEquals(PKG, captor.getValue().getPackage());
+        assertEquals(existing.getId(), captor.getValue().getStringExtra(
+                NotificationManager.EXTRA_BLOCK_STATE_CHANGED_ID));
+        assertTrue(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, false));
+    }
+
+    @Test
+    public void testUpdateGroupNotifyCreatorUnblock() throws Exception {
+        NotificationChannelGroup existing = new NotificationChannelGroup("id", "name");
+        existing.setBlocked(true);
+        mService.setRankingHelper(mRankingHelper);
+        when(mRankingHelper.getNotificationChannelGroup(eq(existing.getId()), eq(PKG), anyInt()))
+                .thenReturn(existing);
+
+        mBinderService.updateNotificationChannelGroupForPackage(
+                PKG, 0, new NotificationChannelGroup("id", "name"));
+        ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+        verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));
+
+        assertEquals(NotificationManager.ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED,
+                captor.getValue().getAction());
+        assertEquals(PKG, captor.getValue().getPackage());
+        assertEquals(existing.getId(), captor.getValue().getStringExtra(
+                NotificationManager.EXTRA_BLOCK_STATE_CHANGED_ID));
+        assertFalse(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, false));
+    }
+
+    @Test
+    public void testUpdateGroupNoNotifyCreatorOtherChanges() throws Exception {
+        NotificationChannelGroup existing = new NotificationChannelGroup("id", "name");
+        mService.setRankingHelper(mRankingHelper);
+        when(mRankingHelper.getNotificationChannelGroup(eq(existing.getId()), eq(PKG), anyInt()))
+                .thenReturn(existing);
+
+        mBinderService.updateNotificationChannelGroupForPackage(
+                PKG, 0, new NotificationChannelGroup("id", "new name"));
+        verify(mContext, never()).sendBroadcastAsUser(any(), any(), eq(null));
+    }
+
+    @Test
     public void testCreateChannelNotifyListener() throws Exception {
         List<String> associations = new ArrayList<>();
         associations.add("a");
@@ -1040,6 +1156,9 @@
         List<String> associations = new ArrayList<>();
         associations.add("a");
         when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
+        when(mRankingHelper.getNotificationChannel(eq(PKG), anyInt(),
+                eq(mTestNotificationChannel.getId()), anyBoolean()))
+                .thenReturn(mTestNotificationChannel);
 
         mBinderService.updateNotificationChannelFromPrivilegedListener(
                 null, PKG, Process.myUserHandle(), mTestNotificationChannel);
diff --git a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
index 3c02e23..2d03f11 100644
--- a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
@@ -372,7 +372,7 @@
                 mHelper.getNotificationChannel(PKG, UID, channel2.getId(), false));
 
         List<NotificationChannelGroup> actualGroups =
-                mHelper.getNotificationChannelGroups(PKG, UID, false).getList();
+                mHelper.getNotificationChannelGroups(PKG, UID, false, true).getList();
         boolean foundNcg = false;
         for (NotificationChannelGroup actual : actualGroups) {
             if (ncg.getId().equals(actual.getId())) {
@@ -442,7 +442,7 @@
                 mHelper.getNotificationChannel(PKG, UID, channel3.getId(), false));
 
         List<NotificationChannelGroup> actualGroups =
-                mHelper.getNotificationChannelGroups(PKG, UID, false).getList();
+                mHelper.getNotificationChannelGroups(PKG, UID, false, true).getList();
         boolean foundNcg = false;
         for (NotificationChannelGroup actual : actualGroups) {
             if (ncg.getId().equals(actual.getId())) {
@@ -1281,7 +1281,8 @@
 
         mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{PKG}, new int[]{UID});
 
-        assertEquals(0, mHelper.getNotificationChannelGroups(PKG, UID, true).getList().size());
+        assertEquals(0,
+                mHelper.getNotificationChannelGroups(PKG, UID, true, true).getList().size());
     }
 
     @Test
@@ -1370,7 +1371,7 @@
         mHelper.createNotificationChannel(PKG, UID, channel3, true);
 
         List<NotificationChannelGroup> actual =
-                mHelper.getNotificationChannelGroups(PKG, UID, true).getList();
+                mHelper.getNotificationChannelGroups(PKG, UID, true, true).getList();
         assertEquals(3, actual.size());
         for (NotificationChannelGroup group : actual) {
             if (group.getId() == null) {
@@ -1402,13 +1403,13 @@
                 new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
         channel1.setGroup(ncg.getId());
         mHelper.createNotificationChannel(PKG, UID, channel1, true);
-        mHelper.getNotificationChannelGroups(PKG, UID, true).getList();
+        mHelper.getNotificationChannelGroups(PKG, UID, true, true).getList();
 
         channel1.setImportance(IMPORTANCE_LOW);
         mHelper.updateNotificationChannel(PKG, UID, channel1, true);
 
         List<NotificationChannelGroup> actual =
-                mHelper.getNotificationChannelGroups(PKG, UID, true).getList();
+                mHelper.getNotificationChannelGroups(PKG, UID, true, true).getList();
 
         assertEquals(2, actual.size());
         for (NotificationChannelGroup group : actual) {