Ensure that SysUI & listeners get the newly updated bubble state

This CL fixes two issues:

1) When bubbles are posted we should note that the notification has
   interrupted
2) When the bubble flag state changes it gets propagated to any listeners

Fixes: 131187135
Test: atest NotificationManagerServiceTest
Change-Id: I003a36b9938a4d1f4a69efea463a39fd0df1abda
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 48edf67..e8b054e 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -40,6 +40,7 @@
 import android.app.ActivityTaskManager;
 import android.app.IActivityTaskManager;
 import android.app.Notification;
+import android.app.NotificationManager;
 import android.content.Context;
 import android.content.pm.ParceledListSlice;
 import android.content.res.Configuration;
@@ -381,6 +382,10 @@
      * @param notif the notification associated with this bubble.
      */
     void updateBubble(NotificationEntry notif) {
+        // If this is an interruptive notif, mark that it's interrupted
+        if (notif.importance >= NotificationManager.IMPORTANCE_HIGH) {
+            notif.setInterruption();
+        }
         mBubbleData.notificationEntryUpdated(notif);
     }
 
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 4f85941..13b4ab9 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -23,6 +23,7 @@
 import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
 import static android.app.Notification.FLAG_NO_CLEAR;
 import static android.app.Notification.FLAG_ONGOING_EVENT;
+import static android.app.Notification.FLAG_ONLY_ALERT_ONCE;
 import static android.app.NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED;
 import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED;
 import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED;
@@ -1031,12 +1032,19 @@
                     final StatusBarNotification n = r.sbn;
                     final int callingUid = n.getUid();
                     final String pkg = n.getPackageName();
+                    final boolean wasBubble = r.getNotification().isBubbleNotification();
                     if (isBubble && isNotificationAppropriateToBubble(r, pkg, callingUid,
                             null /* oldEntry */)) {
                         r.getNotification().flags |= FLAG_BUBBLE;
                     } else {
                         r.getNotification().flags &= ~FLAG_BUBBLE;
                     }
+                    if (wasBubble != r.getNotification().isBubbleNotification()) {
+                        // Add the "alert only once" flag so that the notification won't HUN
+                        // unnecessarily just because the bubble flag was changed.
+                        r.getNotification().flags |= FLAG_ONLY_ALERT_ONCE;
+                        mListeners.notifyPostedLocked(r, r);
+                    }
                 }
             }
         }
@@ -5732,7 +5740,7 @@
         }
         // Suppressed because it's a silent update
         final Notification notification = record.getNotification();
-        if (record.isUpdate && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0) {
+        if (record.isUpdate && (notification.flags & FLAG_ONLY_ALERT_ONCE) != 0) {
             return false;
         }
         // Suppressed because another notification in its group handles alerting
@@ -5751,7 +5759,7 @@
     boolean shouldMuteNotificationLocked(final NotificationRecord record) {
         // Suppressed because it's a silent update
         final Notification notification = record.getNotification();
-        if (record.isUpdate && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0) {
+        if (record.isUpdate && (notification.flags & FLAG_ONLY_ALERT_ONCE) != 0) {
             return true;
         }
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index d2332bf..3661e89 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -22,6 +22,7 @@
 import static android.app.Notification.CATEGORY_CALL;
 import static android.app.Notification.FLAG_BUBBLE;
 import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
+import static android.app.Notification.FLAG_ONLY_ALERT_ONCE;
 import static android.app.NotificationManager.EXTRA_BLOCKED_STATE;
 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
 import static android.app.NotificationManager.IMPORTANCE_HIGH;
@@ -119,7 +120,6 @@
 import android.service.notification.Adjustment;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationStats;
-import android.service.notification.NotifyingApp;
 import android.service.notification.StatusBarNotification;
 import android.service.notification.ZenPolicy;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -165,10 +165,8 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import java.util.function.Consumer;
 
 @SmallTest
@@ -5012,6 +5010,9 @@
                 nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
         waitForIdle();
 
+        // Reset as this is called when the notif is first sent
+        reset(mListeners);
+
         // First we were a bubble
         StatusBarNotification[] notifsBefore = mBinderService.getActiveNotifications(PKG);
         assertEquals(1, notifsBefore.length);
@@ -5021,10 +5022,13 @@
         mService.mNotificationDelegate.onNotificationBubbleChanged(nr.getKey(), false);
         waitForIdle();
 
-        // Now we are not a bubble
-        StatusBarNotification[] notifsAfter = mBinderService.getActiveNotifications(PKG);
-        assertEquals(1, notifsAfter.length);
-        assertEquals((notifsAfter[0].getNotification().flags & FLAG_BUBBLE), 0);
+        // Make sure we are not a bubble / reported as such to listeners
+        ArgumentCaptor<NotificationRecord> captor =
+                ArgumentCaptor.forClass(NotificationRecord.class);
+        verify(mListeners, times(1)).notifyPostedLocked(captor.capture(), any());
+
+        assertEquals((captor.getValue().getNotification().flags & FLAG_BUBBLE), 0);
+        assertTrue((captor.getValue().getNotification().flags & FLAG_ONLY_ALERT_ONCE) != 0);
     }
 
     @Test
@@ -5054,14 +5058,20 @@
         when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn(
                 IMPORTANCE_FOREGROUND);
 
+        // Reset as this is called when the notif is first sent
+        reset(mListeners);
+
         // Notify we are now a bubble
         mService.mNotificationDelegate.onNotificationBubbleChanged(nr.getKey(), true);
         waitForIdle();
 
-        // Make sure we are a bubble
-        StatusBarNotification[] notifsAfter = mBinderService.getActiveNotifications(PKG);
-        assertEquals(1, notifsAfter.length);
-        assertTrue((notifsAfter[0].getNotification().flags & FLAG_BUBBLE) != 0);
+        // Make sure we are a bubble / reported as such to listeners
+        ArgumentCaptor<NotificationRecord> captor =
+                ArgumentCaptor.forClass(NotificationRecord.class);
+        verify(mListeners, times(1)).notifyPostedLocked(captor.capture(), any());
+
+        assertTrue((captor.getValue().getNotification().flags & FLAG_BUBBLE) != 0);
+        assertTrue((captor.getValue().getNotification().flags & FLAG_ONLY_ALERT_ONCE) != 0);
     }
 
     @Test
@@ -5082,6 +5092,9 @@
                 nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
         waitForIdle();
 
+        // Reset as this is called when the notif is first sent
+        reset(mListeners);
+
         // Would be a normal notification because wouldn't have met requirements to bubble
         StatusBarNotification[] notifsBefore = mBinderService.getActiveNotifications(PKG);
         assertEquals(1, notifsBefore.length);
@@ -5095,6 +5108,7 @@
         StatusBarNotification[] notifsAfter = mBinderService.getActiveNotifications(PKG);
         assertEquals(1, notifsAfter.length);
         assertEquals((notifsAfter[0].getNotification().flags & FLAG_BUBBLE), 0);
+        verify(mListeners, times(0)).notifyPostedLocked(any(), any());
     }
 
     @Test