Inline notif pipeline flag into BubblesManager

This change is a no-op; the flag is now enabled-by-default, so all
removed code paths here are effectively dead.

Bug: 200269355
Test: atest SystemUITests
Change-Id: I5ccc8d1926a5973ba02bfc20c565a18ec100d805
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 366ef26..ba1e057 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -55,8 +55,6 @@
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.QsFrameTranslateModule;
-import com.android.systemui.statusbar.notification.NotifPipelineFlags;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
@@ -220,11 +218,9 @@
             ZenModeController zenModeController,
             NotificationLockscreenUserManager notifUserManager,
             NotificationGroupManagerLegacy groupManager,
-            NotificationEntryManager entryManager,
             CommonNotifCollection notifCollection,
             NotifPipeline notifPipeline,
             SysUiState sysUiState,
-            NotifPipelineFlags notifPipelineFlags,
             DumpManager dumpManager,
             @Main Executor sysuiMainExecutor) {
         return Optional.ofNullable(BubblesManager.create(context,
@@ -240,11 +236,9 @@
                 zenModeController,
                 notifUserManager,
                 groupManager,
-                entryManager,
                 notifCollection,
                 notifPipeline,
                 sysUiState,
-                notifPipelineFlags,
                 dumpManager,
                 sysuiMainExecutor));
     }
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index e90775d..bca2a24 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -21,14 +21,10 @@
 import static android.provider.Settings.Secure.NOTIFICATION_BUBBLES;
 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_CANCEL;
-import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
-import static android.service.notification.NotificationListenerService.REASON_CLICK;
 import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
 import static android.service.notification.NotificationStats.DISMISSAL_BUBBLE;
 import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
 
-import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON;
 import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
 import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
 
@@ -55,7 +51,6 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.statusbar.NotificationVisibility;
 import com.android.systemui.Dumpable;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dump.DumpManager;
@@ -63,9 +58,7 @@
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
-import com.android.systemui.statusbar.notification.NotifPipelineFlags;
 import com.android.systemui.statusbar.notification.NotificationChannelHelper;
-import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotifCollection;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -114,7 +107,6 @@
     private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
     private final NotificationLockscreenUserManager mNotifUserManager;
     private final NotificationGroupManagerLegacy mNotificationGroupManager;
-    private final NotificationEntryManager mNotificationEntryManager;
     private final CommonNotifCollection mCommonNotifCollection;
     private final NotifPipeline mNotifPipeline;
     private final Executor mSysuiMainExecutor;
@@ -142,11 +134,9 @@
             ZenModeController zenModeController,
             NotificationLockscreenUserManager notifUserManager,
             NotificationGroupManagerLegacy groupManager,
-            NotificationEntryManager entryManager,
             CommonNotifCollection notifCollection,
             NotifPipeline notifPipeline,
             SysUiState sysUiState,
-            NotifPipelineFlags notifPipelineFlags,
             DumpManager dumpManager,
             Executor sysuiMainExecutor) {
         if (bubblesOptional.isPresent()) {
@@ -163,11 +153,9 @@
                     zenModeController,
                     notifUserManager,
                     groupManager,
-                    entryManager,
                     notifCollection,
                     notifPipeline,
                     sysUiState,
-                    notifPipelineFlags,
                     dumpManager,
                     sysuiMainExecutor);
         } else {
@@ -189,11 +177,9 @@
             ZenModeController zenModeController,
             NotificationLockscreenUserManager notifUserManager,
             NotificationGroupManagerLegacy groupManager,
-            NotificationEntryManager entryManager,
             CommonNotifCollection notifCollection,
             NotifPipeline notifPipeline,
             SysUiState sysUiState,
-            NotifPipelineFlags notifPipelineFlags,
             DumpManager dumpManager,
             Executor sysuiMainExecutor) {
         mContext = context;
@@ -205,7 +191,6 @@
         mNotificationInterruptStateProvider = interruptionStateProvider;
         mNotifUserManager = notifUserManager;
         mNotificationGroupManager = groupManager;
-        mNotificationEntryManager = entryManager;
         mCommonNotifCollection = notifCollection;
         mNotifPipeline = notifPipeline;
         mSysuiMainExecutor = sysuiMainExecutor;
@@ -215,11 +200,7 @@
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE))
                 : statusBarService;
 
-        if (notifPipelineFlags.isNewPipelineEnabled()) {
-            setupNotifPipeline();
-        } else {
-            setupNEM();
-        }
+        setupNotifPipeline();
 
         dumpManager.registerDumpable(TAG, this);
 
@@ -438,141 +419,6 @@
         mBubbles.setSysuiProxy(mSysuiProxy);
     }
 
-    private void setupNEM() {
-        mNotificationEntryManager.addNotificationEntryListener(
-                new NotificationEntryListener() {
-                    @Override
-                    public void onPendingEntryAdded(NotificationEntry entry) {
-                        BubblesManager.this.onEntryAdded(entry);
-                    }
-
-                    @Override
-                    public void onPreEntryUpdated(NotificationEntry entry) {
-                        BubblesManager.this.onEntryUpdated(entry);
-                    }
-
-                    @Override
-                    public void onEntryRemoved(
-                            NotificationEntry entry,
-                            @Nullable NotificationVisibility visibility,
-                            boolean removedByUser,
-                            int reason) {
-                        BubblesManager.this.onEntryRemoved(entry);
-                    }
-
-                    @Override
-                    public void onNotificationRankingUpdated(RankingMap rankingMap) {
-                        BubblesManager.this.onRankingUpdate(rankingMap);
-                    }
-
-                    @Override
-                    public void onNotificationChannelModified(
-                            String pkgName,
-                            UserHandle user,
-                            NotificationChannel channel,
-                            int modificationType) {
-                        BubblesManager.this.onNotificationChannelModified(pkgName,
-                                user,
-                                channel,
-                                modificationType);
-                    }
-                });
-
-        // The new pipeline takes care of this as a NotifDismissInterceptor BubbleCoordinator
-        mNotificationEntryManager.addNotificationRemoveInterceptor(
-                (key, entry, dismissReason) -> {
-                    final boolean isClearAll = dismissReason == REASON_CANCEL_ALL;
-                    final boolean isUserDismiss = dismissReason == REASON_CANCEL
-                            || dismissReason == REASON_CLICK;
-                    final boolean isAppCancel = dismissReason == REASON_APP_CANCEL
-                            || dismissReason == REASON_APP_CANCEL_ALL;
-                    final boolean isSummaryCancel =
-                            dismissReason == REASON_GROUP_SUMMARY_CANCELED;
-
-                    // Need to check for !appCancel here because the notification may have
-                    // previously been dismissed & entry.isRowDismissed would still be true
-                    boolean userRemovedNotif =
-                            (entry != null && entry.isRowDismissed() && !isAppCancel)
-                                    || isClearAll || isUserDismiss || isSummaryCancel;
-
-                    if (userRemovedNotif) {
-                        return handleDismissalInterception(entry);
-                    }
-                    return false;
-                });
-
-        mNotificationGroupManager.registerGroupChangeListener(
-                new NotificationGroupManagerLegacy.OnGroupChangeListener() {
-                    @Override
-                    public void onGroupSuppressionChanged(
-                            NotificationGroupManagerLegacy.NotificationGroup group,
-                            boolean suppressed) {
-                        // More notifications could be added causing summary to no longer
-                        // be suppressed -- in this case need to remove the key.
-                        final String groupKey = group.summary != null
-                                ? group.summary.getSbn().getGroupKey()
-                                : null;
-                        if (!suppressed && groupKey != null) {
-                            mBubbles.removeSuppressedSummaryIfNecessary(groupKey, null, null);
-                        }
-                    }
-                });
-
-        addNotifCallback(new NotifCallback() {
-            @Override
-            public void removeNotification(NotificationEntry entry,
-                    DismissedByUserStats dismissedByUserStats, int reason) {
-                mNotificationEntryManager.performRemoveNotification(entry.getSbn(),
-                        dismissedByUserStats, reason);
-            }
-
-            @Override
-            public void invalidateNotifications(String reason) {
-                mNotificationEntryManager.updateNotifications(reason);
-            }
-
-            @Override
-            public void maybeCancelSummary(NotificationEntry entry) {
-                // Check if removed bubble has an associated suppressed group summary that needs
-                // to be removed now.
-                final String groupKey = entry.getSbn().getGroupKey();
-                mBubbles.removeSuppressedSummaryIfNecessary(groupKey, (summaryKey) -> {
-                    final NotificationEntry summary =
-                            mNotificationEntryManager.getActiveNotificationUnfiltered(summaryKey);
-                    if (summary != null) {
-                        mNotificationEntryManager.performRemoveNotification(
-                                summary.getSbn(),
-                                getDismissedByUserStats(summary, false),
-                                UNDEFINED_DISMISS_REASON);
-                    }
-                }, mSysuiMainExecutor);
-
-                // Check if we still need to remove the summary from NoManGroup because the summary
-                // may not be in the mBubbleData.mSuppressedGroupKeys list and removed above.
-                // For example:
-                // 1. Bubbled notifications (group) is posted to shade and are visible bubbles
-                // 2. User expands bubbles so now their respective notifications in the shade are
-                // hidden, including the group summary
-                // 3. User removes all bubbles
-                // 4. We expect all the removed bubbles AND the summary (note: the summary was
-                // never added to the suppressedSummary list in BubbleData, so we add this check)
-                NotificationEntry summary = mNotificationGroupManager.getLogicalGroupSummary(entry);
-                if (summary != null) {
-                    ArrayList<NotificationEntry> summaryChildren =
-                            mNotificationGroupManager.getLogicalChildren(summary.getSbn());
-                    boolean isSummaryThisNotif = summary.getKey().equals(entry.getKey());
-                    if (!isSummaryThisNotif && (summaryChildren == null
-                            || summaryChildren.isEmpty())) {
-                        mNotificationEntryManager.performRemoveNotification(
-                                summary.getSbn(),
-                                getDismissedByUserStats(summary, false),
-                                UNDEFINED_DISMISS_REASON);
-                    }
-                }
-            }
-        });
-    }
-
     private void setupNotifPipeline() {
         mNotifPipeline.addCollectionListener(new NotifCollectionListener() {
             @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 7d4e27f..2e58fc4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -21,12 +21,9 @@
 import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED;
 import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
 import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
-import static android.service.notification.NotificationListenerService.REASON_CANCEL;
-import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
 import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.wm.shell.bubbles.Bubbles.DISMISS_NOTIF_CANCEL;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -62,7 +59,6 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.hardware.display.AmbientDisplayConfiguration;
-import android.hardware.face.FaceManager;
 import android.os.Handler;
 import android.os.PowerManager;
 import android.os.UserHandle;
@@ -90,18 +86,16 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationRemoveInterceptor;
 import com.android.systemui.statusbar.RankingBuilder;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.NotifPipelineFlags;
-import com.android.systemui.statusbar.notification.NotificationEntryListener;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationFilter;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
 import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
 import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptLogger;
@@ -118,7 +112,6 @@
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.TaskViewTransitions;
 import com.android.wm.shell.WindowManagerShellWrapper;
@@ -140,8 +133,6 @@
 import com.android.wm.shell.draganddrop.DragAndDropController;
 import com.android.wm.shell.onehanded.OneHandedController;
 
-import com.google.common.collect.ImmutableList;
-
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
@@ -155,22 +146,17 @@
 import java.util.List;
 import java.util.Optional;
 
-/**
- * Tests the NotificationEntryManager setup with BubbleController.
- * The {@link NotifPipeline} setup with BubbleController is tested in
- * {@link NewNotifPipelineBubblesTest}.
- */
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class BubblesTest extends SysuiTestCase {
     @Mock
-    private NotificationEntryManager mNotificationEntryManager;
-    @Mock
     private CommonNotifCollection mCommonNotifCollection;
     @Mock
     private NotificationGroupManagerLegacy mNotificationGroupManager;
     @Mock
+    private BubblesManager.NotifCallback mNotifCallback;
+    @Mock
     private WindowManager mWindowManager;
     @Mock
     private IActivityManager mActivityManager;
@@ -183,8 +169,6 @@
     @Mock
     private ZenModeConfig mZenModeConfig;
     @Mock
-    private FaceManager mFaceManager;
-    @Mock
     private NotificationLockscreenUserManager mLockscreenUserManager;
     @Mock
     private SysuiStatusBarStateController mStatusBarStateController;
@@ -196,15 +180,17 @@
     private FloatingContentCoordinator mFloatingContentCoordinator;
     @Mock
     private BubbleDataRepository mDataRepository;
+    @Mock
+    private NotificationShadeWindowView mNotificationShadeWindowView;
+    @Mock
+    private AuthController mAuthController;
 
     private SysUiState mSysUiState;
     private boolean mSysUiStateBubblesExpanded;
     private boolean mSysUiStateBubblesManageMenuExpanded;
 
     @Captor
-    private ArgumentCaptor<NotificationEntryListener> mEntryListenerCaptor;
-    @Captor
-    private ArgumentCaptor<NotificationRemoveInterceptor> mRemoveInterceptorCaptor;
+    private ArgumentCaptor<NotifCollectionListener> mNotifListenerCaptor;
     @Captor
     private ArgumentCaptor<List<Bubble>> mBubbleListCaptor;
     @Captor
@@ -212,22 +198,16 @@
     @Captor
     private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverArgumentCaptor;
 
-
     private BubblesManager mBubblesManager;
-    // TODO(178618782): Move tests on the controller directly to the shell
     private TestableBubbleController mBubbleController;
     private NotificationShadeWindowControllerImpl mNotificationShadeWindowController;
-    private NotificationEntryListener mEntryListener;
-    private NotificationRemoveInterceptor mRemoveInterceptor;
-
+    private NotifCollectionListener mEntryListener;
     private NotificationTestHelper mNotificationTestHelper;
     private NotificationEntry mRow;
     private NotificationEntry mRow2;
-    private NotificationEntry mRow3;
     private ExpandableNotificationRow mNonBubbleNotifRow;
     private BubbleEntry mBubbleEntry;
     private BubbleEntry mBubbleEntry2;
-    private BubbleEntry mBubbleEntry3;
 
     private BubbleEntry mBubbleEntryUser11;
     private BubbleEntry mBubbleEntry2User11;
@@ -245,12 +225,8 @@
     @Mock
     private NotifPipeline mNotifPipeline;
     @Mock
-    private NotifPipelineFlags mNotifPipelineFlags;
-    @Mock
     private DumpManager mDumpManager;
     @Mock
-    private NotificationShadeWindowView mNotificationShadeWindowView;
-    @Mock
     private IStatusBarService mStatusBarService;
     @Mock
     private NotificationVisibilityProvider mVisibilityProvider;
@@ -269,8 +245,6 @@
     @Mock
     private ScreenOffAnimationController mScreenOffAnimationController;
     @Mock
-    private AuthController mAuthController;
-    @Mock
     private TaskViewTransitions mTaskViewTransitions;
     @Mock
     private Optional<OneHandedController> mOneHandedOptional;
@@ -290,7 +264,6 @@
         // For the purposes of this test, just run everything synchronously
         ShellExecutor syncExecutor = new SyncExecutor();
 
-        mContext.addMockSystemService(FaceManager.class, mFaceManager);
         when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors);
 
         mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext,
@@ -308,11 +281,9 @@
                 TestableLooper.get(this));
         mRow = mNotificationTestHelper.createBubble(mDeleteIntent);
         mRow2 = mNotificationTestHelper.createBubble(mDeleteIntent);
-        mRow3 = mNotificationTestHelper.createBubble(mDeleteIntent);
         mNonBubbleNotifRow = mNotificationTestHelper.createRow();
         mBubbleEntry = BubblesManager.notifToBubbleEntry(mRow);
         mBubbleEntry2 = BubblesManager.notifToBubbleEntry(mRow2);
-        mBubbleEntry3 = BubblesManager.notifToBubbleEntry(mRow3);
 
         UserHandle handle = mock(UserHandle.class);
         when(handle.getIdentifier()).thenReturn(11);
@@ -321,9 +292,6 @@
         mBubbleEntry2User11 = BubblesManager.notifToBubbleEntry(
                 mNotificationTestHelper.createBubble(handle));
 
-        // Return non-null notification data from the CommonNotifCollection
-        when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow);
-
         mZenModeConfig.suppressedVisualEffects = 0;
         when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);
 
@@ -336,7 +304,6 @@
                     (sysUiFlags & QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED) != 0;
         });
 
-        // TODO: Fix
         mPositioner = new TestableBubblePositioner(mContext, mWindowManager);
         mPositioner.setMaxBubbles(5);
         mBubbleData = new BubbleData(mContext, mBubbleLogger, mPositioner, syncExecutor);
@@ -355,8 +322,6 @@
                         mock(NotifPipelineFlags.class),
                         mock(KeyguardNotificationVisibilityProvider.class)
                 );
-
-        when(mNotifPipelineFlags.isNewPipelineEnabled()).thenReturn(false);
         when(mShellTaskOrganizer.getExecutor()).thenReturn(syncExecutor);
         mBubbleController = new TestableBubbleController(
                 mContext,
@@ -396,23 +361,17 @@
                 mZenModeController,
                 mLockscreenUserManager,
                 mNotificationGroupManager,
-                mNotificationEntryManager,
                 mCommonNotifCollection,
                 mNotifPipeline,
                 mSysUiState,
-                mNotifPipelineFlags,
                 mDumpManager,
                 syncExecutor);
+        mBubblesManager.addNotifCallback(mNotifCallback);
 
-        // XXX: Does *this* need to be changed?
         // Get a reference to the BubbleController's entry listener
-        verify(mNotificationEntryManager, atLeastOnce())
-                .addNotificationEntryListener(mEntryListenerCaptor.capture());
-        mEntryListener = mEntryListenerCaptor.getValue();
-        // And the remove interceptor
-        verify(mNotificationEntryManager, atLeastOnce())
-                .addNotificationRemoveInterceptor(mRemoveInterceptorCaptor.capture());
-        mRemoveInterceptor = mRemoveInterceptorCaptor.getValue();
+        verify(mNotifPipeline, atLeastOnce())
+                .addCollectionListener(mNotifListenerCaptor.capture());
+        mEntryListener = mNotifListenerCaptor.getValue();
     }
 
     @Test
@@ -433,90 +392,75 @@
     @Test
     public void testRemoveBubble() {
         mBubbleController.updateBubble(mBubbleEntry);
-        assertNotNull(mBubbleData.getBubbleInStackWithKey(mBubbleEntry.getKey()));
+        assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow.getKey()));
         assertTrue(mBubbleController.hasBubbles());
-        verify(mNotificationEntryManager).updateNotifications(any());
+        verify(mNotifCallback, times(1)).invalidateNotifications(anyString());
 
         mBubbleController.removeBubble(
                 mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
         assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getKey()));
-        verify(mNotificationEntryManager, times(2)).updateNotifications(anyString());
+        verify(mNotifCallback, times(2)).invalidateNotifications(anyString());
 
         assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
     }
 
     @Test
-    public void testPromoteBubble_autoExpand() throws Exception {
-        mBubbleController.updateBubble(mBubbleEntry2);
+    public void testRemoveBubble_withDismissedNotif_inOverflow() {
+        mEntryListener.onEntryAdded(mRow);
         mBubbleController.updateBubble(mBubbleEntry);
-        when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow);
-        when(mCommonNotifCollection.getEntry(mRow2.getKey())).thenReturn(mRow2);
-        mBubbleController.removeBubble(
-                mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
 
-        Bubble b = mBubbleData.getOverflowBubbleWithKey(mRow.getKey());
-        assertThat(mBubbleData.getOverflowBubbles()).isEqualTo(ImmutableList.of(b));
-        verify(mNotificationEntryManager, never()).performRemoveNotification(
-                eq(mRow.getSbn()), any(),  anyInt());
-        assertThat(mRow.isBubble()).isFalse();
-
-        Bubble b2 = mBubbleData.getBubbleInStackWithKey(mRow2.getKey());
-        assertThat(mBubbleData.getSelectedBubble()).isEqualTo(b2);
-
-        mBubbleController.promoteBubbleFromOverflow(b);
-
-        assertThat(b.isBubble()).isTrue();
-        assertThat(b.shouldAutoExpand()).isTrue();
-        int flags = Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE
-                | Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
-        verify(mStatusBarService, times(1)).onNotificationBubbleChanged(
-                eq(b.getKey()), eq(true), eq(flags));
-    }
-
-    @Test
-    public void testCancelOverflowBubble() {
-        mBubbleController.updateBubble(mBubbleEntry2);
-        mBubbleController.updateBubble(mBubbleEntry, /* suppressFlyout */
-                false, /* showInShade */ true);
-        when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow);
-        when(mCommonNotifCollection.getEntry(mRow2.getKey())).thenReturn(mRow2);
-        mBubbleController.removeBubble(
-                mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
-
-        mBubbleController.removeBubble(
-                mRow.getKey(), DISMISS_NOTIF_CANCEL);
-        verify(mNotificationEntryManager, times(1)).performRemoveNotification(
-                eq(mRow.getSbn()), any(), anyInt());
-        assertThat(mBubbleData.getOverflowBubbles()).isEmpty();
-        assertFalse(mRow.isBubble());
-    }
-
-    @Test
-    public void testUserChange_doesNotRemoveNotif() {
-        mBubbleController.updateBubble(mBubbleEntry);
         assertTrue(mBubbleController.hasBubbles());
+        assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
 
+        // Make it look like dismissed notif
+        mBubbleData.getBubbleInStackWithKey(mRow.getKey()).setSuppressNotification(true);
+
+        // Now remove the bubble
         mBubbleController.removeBubble(
-                mRow.getKey(), Bubbles.DISMISS_USER_CHANGED);
-        verify(mNotificationEntryManager, never()).performRemoveNotification(
-                eq(mRow.getSbn()), any(), anyInt());
+                mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
+        assertTrue(mBubbleData.hasOverflowBubbleWithKey(mRow.getKey()));
+
+        // We don't remove the notification since the bubble is still in overflow.
+        verify(mNotifCallback, never()).removeNotification(eq(mRow), any(), anyInt());
         assertFalse(mBubbleController.hasBubbles());
-        assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
-        assertTrue(mRow.isBubble());
+    }
+
+    @Test
+    public void testRemoveBubble_withDismissedNotif_notInOverflow() {
+        mEntryListener.onEntryAdded(mRow);
+        mBubbleController.updateBubble(mBubbleEntry);
+        when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow);
+
+        assertTrue(mBubbleController.hasBubbles());
+        assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
+
+        // Make it look like dismissed notif
+        mBubbleData.getBubbleInStackWithKey(mRow.getKey()).setSuppressNotification(true);
+
+        // Now remove the bubble
+        mBubbleController.removeBubble(
+                mRow.getKey(), Bubbles.DISMISS_NOTIF_CANCEL);
+        assertFalse(mBubbleData.hasOverflowBubbleWithKey(mRow.getKey()));
+
+        // Since the notif is dismissed and not in overflow, once the bubble is removed,
+        // removeNotification gets called to really remove the notif
+        verify(mNotifCallback, times(1)).removeNotification(eq(mRow),
+                any(), anyInt());
+        assertFalse(mBubbleController.hasBubbles());
     }
 
     @Test
     public void testDismissStack() {
         mBubbleController.updateBubble(mBubbleEntry);
-        verify(mNotificationEntryManager, times(1)).updateNotifications(any());
+        verify(mNotifCallback, times(1)).invalidateNotifications(anyString());
         assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow.getKey()));
         mBubbleController.updateBubble(mBubbleEntry2);
-        verify(mNotificationEntryManager, times(2)).updateNotifications(any());
+        verify(mNotifCallback, times(2)).invalidateNotifications(anyString());
         assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow2.getKey()));
         assertTrue(mBubbleController.hasBubbles());
 
         mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE);
-        verify(mNotificationEntryManager, times(3)).updateNotifications(any());
+        verify(mNotifCallback, times(3)).invalidateNotifications(anyString());
         assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getKey()));
         assertNull(mBubbleData.getBubbleInStackWithKey(mRow2.getKey()));
 
@@ -528,7 +472,7 @@
         assertStackCollapsed();
 
         // Mark it as a bubble and add it explicitly
-        mEntryListener.onPendingEntryAdded(mRow);
+        mEntryListener.onEntryAdded(mRow);
         mBubbleController.updateBubble(mBubbleEntry);
 
         // We should have bubbles & their notifs should not be suppressed
@@ -536,7 +480,6 @@
         assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
 
         // Expand the stack
-        BubbleStackView stackView = mBubbleController.getStackView();
         mBubbleData.setExpanded(true);
         assertStackExpanded();
         verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey());
@@ -556,8 +499,8 @@
     @Ignore("Currently broken.")
     public void testCollapseAfterChangingExpandedBubble() {
         // Mark it as a bubble and add it explicitly
-        mEntryListener.onPendingEntryAdded(mRow);
-        mEntryListener.onPendingEntryAdded(mRow2);
+        mEntryListener.onEntryAdded(mRow);
+        mEntryListener.onEntryAdded(mRow2);
         mBubbleController.updateBubble(mBubbleEntry);
         mBubbleController.updateBubble(mBubbleEntry2);
 
@@ -593,6 +536,7 @@
         verify(mBubbleExpandListener, atLeastOnce()).onBubbleExpandChanged(
                 true, mRow.getKey());
 
+
         // Collapse
         mBubbleController.collapseStack();
         assertStackCollapsed();
@@ -602,7 +546,7 @@
     @Test
     public void testExpansionRemovesShowInShadeAndDot() {
         // Mark it as a bubble and add it explicitly
-        mEntryListener.onPendingEntryAdded(mRow);
+        mEntryListener.onEntryAdded(mRow);
         mBubbleController.updateBubble(mBubbleEntry);
 
         // We should have bubbles & their notifs should not be suppressed
@@ -627,7 +571,7 @@
     @Test
     public void testUpdateWhileExpanded_DoesntChangeShowInShadeAndDot() {
         // Mark it as a bubble and add it explicitly
-        mEntryListener.onPendingEntryAdded(mRow);
+        mEntryListener.onEntryAdded(mRow);
         mBubbleController.updateBubble(mBubbleEntry);
 
         // We should have bubbles & their notifs should not be suppressed
@@ -649,7 +593,7 @@
         assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
 
         // Send update
-        mEntryListener.onPreEntryUpdated(mRow);
+        mEntryListener.onEntryUpdated(mRow);
 
         // Nothing should have changed
         // Notif is suppressed after expansion
@@ -661,8 +605,8 @@
     @Test
     public void testRemoveLastExpanded_collapses() {
         // Mark it as a bubble and add it explicitly
-        mEntryListener.onPendingEntryAdded(mRow);
-        mEntryListener.onPendingEntryAdded(mRow2);
+        mEntryListener.onEntryAdded(mRow);
+        mEntryListener.onEntryAdded(mRow2);
         mBubbleController.updateBubble(mBubbleEntry);
         mBubbleController.updateBubble(mBubbleEntry2);
 
@@ -707,7 +651,7 @@
     @Test
     public void testRemoveLastExpandedEmptyOverflow_collapses() {
         // Mark it as a bubble and add it explicitly
-        mEntryListener.onPendingEntryAdded(mRow);
+        mEntryListener.onEntryAdded(mRow);
         mBubbleController.updateBubble(mBubbleEntry);
 
         // Expand
@@ -732,6 +676,7 @@
         assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
     }
 
+
     @Test
     public void testAutoExpand_fails_noFlag() {
         assertStackCollapsed();
@@ -739,7 +684,7 @@
                 Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE, false /* enableFlag */);
 
         // Add the auto expand bubble
-        mEntryListener.onPendingEntryAdded(mRow);
+        mEntryListener.onEntryAdded(mRow);
         mBubbleController.updateBubble(mBubbleEntry);
 
         // Expansion shouldn't change
@@ -755,7 +700,7 @@
                 Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE, true /* enableFlag */);
 
         // Add the auto expand bubble
-        mEntryListener.onPendingEntryAdded(mRow);
+        mEntryListener.onEntryAdded(mRow);
         mBubbleController.updateBubble(mBubbleEntry);
 
         // Expansion should change
@@ -771,7 +716,7 @@
                 Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, true /* enableFlag */);
 
         // Add the suppress notif bubble
-        mEntryListener.onPendingEntryAdded(mRow);
+        mEntryListener.onEntryAdded(mRow);
         mBubbleController.updateBubble(mBubbleEntry);
 
         // Notif should be suppressed because we were foreground
@@ -805,22 +750,8 @@
     }
 
     @Test
-    public void testExpandStackAndSelectBubble_removedFirst() {
-        mEntryListener.onPendingEntryAdded(mRow);
-        mBubbleController.updateBubble(mBubbleEntry);
-
-        // Simulate notification cancellation.
-        mRemoveInterceptor.onNotificationRemoveRequested(
-                mRow.getKey(), mRow, REASON_APP_CANCEL);
-
-        mBubbleController.expandStackAndSelectBubble(mBubbleEntry);
-
-        assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
-    }
-
-    @Test
     public void testMarkNewNotificationAsShowInShade() {
-        mEntryListener.onPendingEntryAdded(mRow);
+        mEntryListener.onEntryAdded(mRow);
         assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
 
         mTestableLooper.processAllMessages();
@@ -829,8 +760,8 @@
 
     @Test
     public void testAddNotif_notBubble() {
-        mEntryListener.onPendingEntryAdded(mNonBubbleNotifRow.getEntry());
-        mEntryListener.onPreEntryUpdated(mNonBubbleNotifRow.getEntry());
+        mEntryListener.onEntryAdded(mNonBubbleNotifRow.getEntry());
+        mEntryListener.onEntryUpdated(mNonBubbleNotifRow.getEntry());
 
         assertThat(mBubbleController.hasBubbles()).isFalse();
     }
@@ -868,48 +799,33 @@
         NotificationListenerService.Ranking ranking = new RankingBuilder(
                 mRow.getRanking()).setCanBubble(false).build();
         mRow.setRanking(ranking);
-        mEntryListener.onPreEntryUpdated(mRow);
+        mEntryListener.onEntryUpdated(mRow);
 
         assertFalse(mBubbleController.hasBubbles());
         verify(mDeleteIntent, never()).send();
     }
 
     @Test
-    public void testRemoveBubble_succeeds_appCancel() {
-        mEntryListener.onPendingEntryAdded(mRow);
-        mBubbleController.updateBubble(mBubbleEntry);
-
-        assertTrue(mBubbleController.hasBubbles());
-
-        boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
-                mRow.getKey(), mRow, REASON_APP_CANCEL);
-
-        // Cancels always remove so no need to intercept
-        assertFalse(intercepted);
-    }
-
-    @Test
     public void testRemoveBubble_entryListenerRemove() {
-        mEntryListener.onPendingEntryAdded(mRow);
+        mEntryListener.onEntryAdded(mRow);
         mBubbleController.updateBubble(mBubbleEntry);
 
         assertTrue(mBubbleController.hasBubbles());
 
         // Removes the notification
-        mEntryListener.onEntryRemoved(mRow, null, false, REASON_APP_CANCEL);
+        mEntryListener.onEntryRemoved(mRow, REASON_APP_CANCEL);
         assertFalse(mBubbleController.hasBubbles());
     }
 
     @Test
-    public void removeBubble_clearAllIntercepted()  {
-        mEntryListener.onPendingEntryAdded(mRow);
+    public void removeBubble_intercepted() {
+        mEntryListener.onEntryAdded(mRow);
         mBubbleController.updateBubble(mBubbleEntry);
 
         assertTrue(mBubbleController.hasBubbles());
         assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
 
-        boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
-                mRow.getKey(), mRow, REASON_CANCEL_ALL);
+        boolean intercepted = mBubblesManager.handleDismissalInterception(mRow);
 
         // Intercept!
         assertTrue(intercepted);
@@ -918,99 +834,51 @@
     }
 
     @Test
-    public void removeBubble_userDismissNotifIntercepted() {
-        mEntryListener.onPendingEntryAdded(mRow);
+    public void removeBubble_dismissIntoOverflow_intercepted() {
+        mEntryListener.onEntryAdded(mRow);
         mBubbleController.updateBubble(mBubbleEntry);
 
         assertTrue(mBubbleController.hasBubbles());
         assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
 
-        boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
-                mRow.getKey(), mRow, REASON_CANCEL);
-
-        // Intercept!
-        assertTrue(intercepted);
-        // Should update show in shade state
-        assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
-    }
-
-    @Test
-    public void removeNotif_inOverflow_intercepted() {
-        // Get bubble with notif in shade.
-        mEntryListener.onPendingEntryAdded(mRow);
-        mBubbleController.updateBubble(mBubbleEntry);
-        assertTrue(mBubbleController.hasBubbles());
-        assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
-
-        // Dismiss the bubble into overflow.
-        mBubbleController.removeBubble(
-                mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
+        // Dismiss the bubble
+        mBubbleController.removeBubble(mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
         assertFalse(mBubbleController.hasBubbles());
 
-        boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
-                mRow.getKey(), mRow, REASON_CANCEL);
+        // Dismiss the notification
+        boolean intercepted = mBubblesManager.handleDismissalInterception(mRow);
 
-        // Notif is no longer a bubble, but still in overflow, so we intercept removal.
+        // Intercept dismissal since bubble is going into overflow
         assertTrue(intercepted);
     }
 
     @Test
-    public void removeNotif_notInOverflow_notIntercepted() {
-        // Get bubble with notif in shade.
-        mEntryListener.onPendingEntryAdded(mRow);
+    public void removeBubble_notIntercepted() {
+        mEntryListener.onEntryAdded(mRow);
         mBubbleController.updateBubble(mBubbleEntry);
 
         assertTrue(mBubbleController.hasBubbles());
         assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
 
-        mBubbleController.removeBubble(
-                mRow.getKey(), Bubbles.DISMISS_NO_LONGER_BUBBLE);
+        // Dismiss the bubble
+        mBubbleController.removeBubble(mRow.getKey(), Bubbles.DISMISS_NOTIF_CANCEL);
         assertFalse(mBubbleController.hasBubbles());
 
-        boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
-                mRow.getKey(), mRow, REASON_CANCEL);
+        // Dismiss the notification
+        boolean intercepted = mBubblesManager.handleDismissalInterception(mRow);
 
-        // Notif is no longer a bubble, so we should not intercept removal.
+        // Not a bubble anymore so we don't intercept dismissal.
         assertFalse(intercepted);
     }
 
     @Test
-    public void testOverflowBubble_maxReached_notInShade_bubbleRemoved() {
-        mBubbleController.updateBubble(
-                mBubbleEntry, /* suppressFlyout */ false, /* showInShade */ false);
-        mBubbleController.updateBubble(
-                mBubbleEntry2, /* suppressFlyout */ false, /* showInShade */ false);
-        mBubbleController.updateBubble(
-                mBubbleEntry3, /* suppressFlyout */ false, /* showInShade */ false);
-        when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow);
-        when(mCommonNotifCollection.getEntry(mRow2.getKey())).thenReturn(mRow2);
-        when(mCommonNotifCollection.getEntry(mRow3.getKey())).thenReturn(mRow3);
-        assertEquals(mBubbleData.getBubbles().size(), 3);
-
-        mBubbleData.setMaxOverflowBubbles(1);
-        mBubbleController.removeBubble(
-                mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
-        assertEquals(mBubbleData.getBubbles().size(), 2);
-        assertEquals(mBubbleData.getOverflowBubbles().size(), 1);
-
-        mBubbleController.removeBubble(
-                mRow2.getKey(), Bubbles.DISMISS_USER_GESTURE);
-        // Overflow max of 1 is reached; mRow is oldest, so it gets removed
-        verify(mNotificationEntryManager, times(1)).performRemoveNotification(
-                eq(mRow.getSbn()), any(), eq(REASON_CANCEL));
-        assertEquals(mBubbleData.getBubbles().size(), 1);
-        assertEquals(mBubbleData.getOverflowBubbles().size(), 1);
-    }
-
-    @Test
     public void testNotifyShadeSuppressionChange_notificationDismiss() {
-        mEntryListener.onPendingEntryAdded(mRow);
+        mEntryListener.onEntryAdded(mRow);
 
         assertTrue(mBubbleController.hasBubbles());
         assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
 
-        mRemoveInterceptor.onNotificationRemoveRequested(
-                mRow.getKey(), mRow, REASON_CANCEL);
+        mBubblesManager.handleDismissalInterception(mRow);
 
         // Should update show in shade state
         assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
@@ -1022,7 +890,7 @@
 
     @Test
     public void testNotifyShadeSuppressionChange_bubbleExpanded() {
-        mEntryListener.onPendingEntryAdded(mRow);
+        mEntryListener.onEntryAdded(mRow);
 
         assertTrue(mBubbleController.hasBubbles());
         assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
@@ -1042,9 +910,9 @@
         // GIVEN a group summary with a bubble child
         ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0);
         ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
+        mEntryListener.onEntryAdded(groupedBubble.getEntry());
         when(mCommonNotifCollection.getEntry(groupedBubble.getEntry().getKey()))
                 .thenReturn(groupedBubble.getEntry());
-        mEntryListener.onPendingEntryAdded(groupedBubble.getEntry());
         groupSummary.addChildNotification(groupedBubble);
         assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
 
@@ -1054,10 +922,10 @@
         // THEN the summary and bubbled child are suppressed from the shade
         assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
                 groupedBubble.getEntry().getKey(),
-                groupSummary.getEntry().getSbn().getGroupKey()));
+                groupedBubble.getEntry().getSbn().getGroupKey()));
         assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade(
                 groupedBubble.getEntry().getKey(),
-                groupSummary.getEntry().getSbn().getGroupKey()));
+                groupedBubble.getEntry().getSbn().getGroupKey()));
         assertTrue(mBubbleData.isSummarySuppressed(groupSummary.getEntry().getSbn().getGroupKey()));
     }
 
@@ -1066,7 +934,7 @@
         // GIVEN a group summary with a bubble child
         ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0);
         ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
-        mEntryListener.onPendingEntryAdded(groupedBubble.getEntry());
+        mEntryListener.onEntryAdded(groupedBubble.getEntry());
         when(mCommonNotifCollection.getEntry(groupedBubble.getEntry().getKey()))
                 .thenReturn(groupedBubble.getEntry());
         groupSummary.addChildNotification(groupedBubble);
@@ -1076,7 +944,7 @@
         mBubblesManager.handleDismissalInterception(groupSummary.getEntry());
 
         // WHEN the summary is cancelled by the app
-        mEntryListener.onEntryRemoved(groupSummary.getEntry(), null, false, REASON_APP_CANCEL);
+        mEntryListener.onEntryRemoved(groupSummary.getEntry(), REASON_APP_CANCEL);
 
         // THEN the summary and its children are removed from bubble data
         assertFalse(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
@@ -1085,14 +953,14 @@
     }
 
     @Test
-    public void testSummaryDismissal_marksBubblesHiddenFromShadeAndDismissesNonBubbledChildren()
+    public void testSummaryDismissalMarksBubblesHiddenFromShadeAndDismissesNonBubbledChildren()
             throws Exception {
         // GIVEN a group summary with two (non-bubble) children and one bubble child
         ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(2);
         ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
+        mEntryListener.onEntryAdded(groupedBubble.getEntry());
         when(mCommonNotifCollection.getEntry(groupedBubble.getEntry().getKey()))
                 .thenReturn(groupedBubble.getEntry());
-        mEntryListener.onPendingEntryAdded(groupedBubble.getEntry());
         groupSummary.addChildNotification(groupedBubble);
 
         // WHEN the summary is dismissed
@@ -1100,16 +968,15 @@
 
         // THEN only the NON-bubble children are dismissed
         List<ExpandableNotificationRow> childrenRows = groupSummary.getAttachedChildren();
-        verify(mNotificationEntryManager, times(1)).performRemoveNotification(
-                eq(childrenRows.get(0).getEntry().getSbn()), any(),
-                eq(REASON_GROUP_SUMMARY_CANCELED));
-        verify(mNotificationEntryManager, times(1)).performRemoveNotification(
-                eq(childrenRows.get(1).getEntry().getSbn()), any(),
-                eq(REASON_GROUP_SUMMARY_CANCELED));
-        verify(mNotificationEntryManager, never()).performRemoveNotification(
-                eq(groupedBubble.getEntry().getSbn()), any(), anyInt());
+        verify(mNotifCallback, times(1)).removeNotification(
+                eq(childrenRows.get(0).getEntry()), any(), eq(REASON_GROUP_SUMMARY_CANCELED));
+        verify(mNotifCallback, times(1)).removeNotification(
+                eq(childrenRows.get(1).getEntry()), any(), eq(REASON_GROUP_SUMMARY_CANCELED));
+        verify(mNotifCallback, never()).removeNotification(eq(groupedBubble.getEntry()),
+                any(), anyInt());
 
-        // THEN the bubble child is suppressed from the shade
+        // THEN the bubble child still exists as a bubble and is suppressed from the shade
+        assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
         assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
                 groupedBubble.getEntry().getKey(),
                 groupedBubble.getEntry().getSbn().getGroupKey()));
@@ -1117,34 +984,17 @@
                 groupedBubble.getEntry().getKey(),
                 groupedBubble.getEntry().getSbn().getGroupKey()));
 
-        // THEN the summary is removed from GroupManager
-        verify(mNotificationGroupManager, times(1)).onEntryRemoved(groupSummary.getEntry());
+        // THEN the summary is also suppressed from the shade
+        assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+                groupSummary.getEntry().getKey(),
+                groupSummary.getEntry().getSbn().getGroupKey()));
+        assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade(
+                groupSummary.getEntry().getKey(),
+                groupSummary.getEntry().getSbn().getGroupKey()));
     }
 
 
     /**
-     * Verifies that when a non visually interruptive update occurs for a bubble in the overflow,
-     * the that bubble does not get promoted from the overflow.
-     */
-    @Test
-    public void test_notVisuallyInterruptive_updateOverflowBubble_notAdded() {
-        // Setup
-        mBubbleController.updateBubble(mBubbleEntry);
-        mBubbleController.updateBubble(mBubbleEntry2);
-        assertTrue(mBubbleController.hasBubbles());
-
-        // Overflow it
-        mBubbleData.dismissBubbleWithKey(mRow.getKey(),
-                Bubbles.DISMISS_USER_GESTURE);
-        assertThat(mBubbleData.hasBubbleInStackWithKey(mRow.getKey())).isFalse();
-        assertThat(mBubbleData.hasOverflowBubbleWithKey(mRow.getKey())).isTrue();
-
-        // Test
-        mBubbleController.updateBubble(mBubbleEntry);
-        assertThat(mBubbleData.hasBubbleInStackWithKey(mRow.getKey())).isFalse();
-    }
-
-    /**
      * Verifies that when the user changes, the bubbles in the overflow list is cleared. Doesn't
      * test the loading from the repository which would be a nice thing to add.
      */
@@ -1185,15 +1035,17 @@
      */
     @Test
     public void testOverflowLoadedOnce() {
-        mBubbleController.updateBubble(mBubbleEntry);
-        mBubbleController.updateBubble(mBubbleEntry2);
-        mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE);
-        assertThat(mBubbleData.getOverflowBubbles().isEmpty()).isFalse();
+        // XXX
+        when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow);
+        when(mCommonNotifCollection.getEntry(mRow2.getKey())).thenReturn(mRow2);
 
-        mBubbleController.updateBubble(mBubbleEntry);
-        mBubbleController.updateBubble(mBubbleEntry2);
-        mBubbleController.removeBubble(mBubbleEntry.getKey(), DISMISS_NOTIF_CANCEL);
-        mBubbleController.removeBubble(mBubbleEntry2.getKey(), DISMISS_NOTIF_CANCEL);
+        mEntryListener.onEntryAdded(mRow);
+        mEntryListener.onEntryAdded(mRow2);
+        mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE);
+        assertThat(mBubbleData.getOverflowBubbles()).isNotEmpty();
+
+        mEntryListener.onEntryRemoved(mRow, REASON_APP_CANCEL);
+        mEntryListener.onEntryRemoved(mRow2, REASON_APP_CANCEL);
         assertThat(mBubbleData.getOverflowBubbles()).isEmpty();
 
         verify(mDataRepository, times(1)).loadBubbles(anyInt(), any());
@@ -1376,6 +1228,7 @@
         assertStackCollapsed();
     }
 
+
     @Test
     public void testRegisterUnregisterBroadcastListener() {
         spyOn(mContext);
@@ -1455,7 +1308,7 @@
 
     @Test
     public void testSetShouldAutoExpand_notifiesFlagChanged() {
-        mEntryListener.onPendingEntryAdded(mRow);
+        mBubbleController.updateBubble(mBubbleEntry);
 
         assertTrue(mBubbleController.hasBubbles());
         Bubble b = mBubbleData.getBubbleInStackWithKey(mBubbleEntry.getKey());
@@ -1551,7 +1404,7 @@
     }
 
     /**
-     * Sets the bubble metadata flags for this entry. These ]flags are normally set by
+     * Sets the bubble metadata flags for this entry. These flags are normally set by
      * NotificationManagerService when the notification is sent, however, these tests do not
      * go through that path so we set them explicitly when testing.
      */
@@ -1570,12 +1423,15 @@
     private Notification.BubbleMetadata getMetadata() {
         Intent target = new Intent(mContext, BubblesTestActivity.class);
         PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, target, FLAG_MUTABLE);
-
-        return new Notification.BubbleMetadata.Builder(bubbleIntent,
-                Icon.createWithResource(mContext, R.drawable.bubble_ic_create_bubble))
+        return new Notification.BubbleMetadata.Builder(
+                bubbleIntent,
+                Icon.createWithResource(
+                        mContext,
+                        com.android.wm.shell.R.drawable.bubble_ic_create_bubble))
                 .build();
     }
 
+
     /**
      * Asserts that the bubble stack is expanded and also validates the cached state is updated.
      */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
deleted file mode 100644
index a6327b9..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
+++ /dev/null
@@ -1,1401 +0,0 @@
-/*
- * Copyright (C) 2020 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 com.android.systemui.wmshell;
-
-import static android.app.Notification.FLAG_BUBBLE;
-import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED;
-import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
-import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
-import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.IActivityManager;
-import android.app.INotificationManager;
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.LauncherApps;
-import android.content.pm.UserInfo;
-import android.hardware.display.AmbientDisplayConfiguration;
-import android.os.Handler;
-import android.os.PowerManager;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.service.dreams.IDreamManager;
-import android.service.notification.NotificationListenerService;
-import android.service.notification.ZenModeConfig;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.util.Pair;
-import android.util.SparseArray;
-import android.view.View;
-import android.view.WindowManager;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.colorextraction.ColorExtractor;
-import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.biometrics.AuthController;
-import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.keyguard.KeyguardViewMediator;
-import com.android.systemui.model.SysUiState;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.RankingBuilder;
-import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.notification.NotifPipelineFlags;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationFilter;
-import com.android.systemui.statusbar.notification.collection.NotifPipeline;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
-import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
-import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
-import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
-import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider;
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptLogger;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
-import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowControllerImpl;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
-import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.TaskViewTransitions;
-import com.android.wm.shell.WindowManagerShellWrapper;
-import com.android.wm.shell.bubbles.Bubble;
-import com.android.wm.shell.bubbles.BubbleData;
-import com.android.wm.shell.bubbles.BubbleDataRepository;
-import com.android.wm.shell.bubbles.BubbleEntry;
-import com.android.wm.shell.bubbles.BubbleLogger;
-import com.android.wm.shell.bubbles.BubbleStackView;
-import com.android.wm.shell.bubbles.Bubbles;
-import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.common.FloatingContentCoordinator;
-import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.common.TaskStackListenerImpl;
-import com.android.wm.shell.draganddrop.DragAndDropController;
-import com.android.wm.shell.onehanded.OneHandedController;
-
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Optional;
-
-/**
- * Tests the NotifPipeline setup with BubbleController.
- * The NotificationEntryManager setup with BubbleController is tested in
- * {@link BubblesTest}.
- */
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
-public class NewNotifPipelineBubblesTest extends SysuiTestCase {
-    @Mock
-    private NotificationEntryManager mNotificationEntryManager;
-    @Mock
-    private CommonNotifCollection mCommonNotifCollection;
-    @Mock
-    private NotificationGroupManagerLegacy mNotificationGroupManager;
-    @Mock
-    private BubblesManager.NotifCallback mNotifCallback;
-    @Mock
-    private WindowManager mWindowManager;
-    @Mock
-    private IActivityManager mActivityManager;
-    @Mock
-    private DozeParameters mDozeParameters;
-    @Mock
-    private ConfigurationController mConfigurationController;
-    @Mock
-    private ZenModeController mZenModeController;
-    @Mock
-    private ZenModeConfig mZenModeConfig;
-    @Mock
-    private NotificationLockscreenUserManager mLockscreenUserManager;
-    @Mock
-    private SysuiStatusBarStateController mStatusBarStateController;
-    @Mock
-    private KeyguardViewMediator mKeyguardViewMediator;
-    @Mock
-    private KeyguardBypassController mKeyguardBypassController;
-    @Mock
-    private FloatingContentCoordinator mFloatingContentCoordinator;
-    @Mock
-    private BubbleDataRepository mDataRepository;
-    @Mock
-    private NotificationShadeWindowView mNotificationShadeWindowView;
-    @Mock
-    private AuthController mAuthController;
-
-    private SysUiState mSysUiState;
-    private boolean mSysUiStateBubblesExpanded;
-    private boolean mSysUiStateBubblesManageMenuExpanded;
-
-    @Captor
-    private ArgumentCaptor<NotifCollectionListener> mNotifListenerCaptor;
-    @Captor
-    private ArgumentCaptor<List<Bubble>> mBubbleListCaptor;
-    @Captor
-    private ArgumentCaptor<IntentFilter> mFilterArgumentCaptor;
-    @Captor
-    private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverArgumentCaptor;
-
-    private BubblesManager mBubblesManager;
-    private TestableBubbleController mBubbleController;
-    private NotificationShadeWindowControllerImpl mNotificationShadeWindowController;
-    private NotifCollectionListener mEntryListener;
-    private NotificationTestHelper mNotificationTestHelper;
-    private NotificationEntry mRow;
-    private NotificationEntry mRow2;
-    private ExpandableNotificationRow mNonBubbleNotifRow;
-    private BubbleEntry mBubbleEntry;
-    private BubbleEntry mBubbleEntry2;
-
-    private BubbleEntry mBubbleEntryUser11;
-    private BubbleEntry mBubbleEntry2User11;
-
-    @Mock
-    private Bubbles.BubbleExpandListener mBubbleExpandListener;
-    @Mock
-    private PendingIntent mDeleteIntent;
-    @Mock
-    private SysuiColorExtractor mColorExtractor;
-    @Mock
-    ColorExtractor.GradientColors mGradientColors;
-    @Mock
-    private ShadeController mShadeController;
-    @Mock
-    private NotifPipeline mNotifPipeline;
-    @Mock
-    private NotifPipelineFlags mNotifPipelineFlags;
-    @Mock
-    private DumpManager mDumpManager;
-    @Mock
-    private IStatusBarService mStatusBarService;
-    @Mock
-    private NotificationVisibilityProvider mVisibilityProvider;
-    @Mock
-    private LauncherApps mLauncherApps;
-    @Mock
-    private WindowManagerShellWrapper mWindowManagerShellWrapper;
-    @Mock
-    private BubbleLogger mBubbleLogger;
-    @Mock
-    private TaskStackListenerImpl mTaskStackListener;
-    @Mock
-    private ShellTaskOrganizer mShellTaskOrganizer;
-    @Mock
-    private KeyguardStateController mKeyguardStateController;
-    @Mock
-    private ScreenOffAnimationController mScreenOffAnimationController;
-    @Mock
-    private TaskViewTransitions mTaskViewTransitions;
-    @Mock
-    private Optional<OneHandedController> mOneHandedOptional;
-
-    private TestableBubblePositioner mPositioner;
-
-    private BubbleData mBubbleData;
-
-    private TestableLooper mTestableLooper;
-
-    @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-
-        mTestableLooper = TestableLooper.get(this);
-
-        // For the purposes of this test, just run everything synchronously
-        ShellExecutor syncExecutor = new SyncExecutor();
-
-        when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors);
-
-        mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext,
-                mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
-                mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController,
-                mColorExtractor, mDumpManager, mKeyguardStateController,
-                mScreenOffAnimationController, mAuthController);
-        mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
-        mNotificationShadeWindowController.attach();
-
-        // Need notifications for bubbles
-        mNotificationTestHelper = new NotificationTestHelper(
-                mContext,
-                mDependency,
-                TestableLooper.get(this));
-        mRow = mNotificationTestHelper.createBubble(mDeleteIntent);
-        mRow2 = mNotificationTestHelper.createBubble(mDeleteIntent);
-        mNonBubbleNotifRow = mNotificationTestHelper.createRow();
-        mBubbleEntry = BubblesManager.notifToBubbleEntry(mRow);
-        mBubbleEntry2 = BubblesManager.notifToBubbleEntry(mRow2);
-
-        UserHandle handle = mock(UserHandle.class);
-        when(handle.getIdentifier()).thenReturn(11);
-        mBubbleEntryUser11 = BubblesManager.notifToBubbleEntry(
-                mNotificationTestHelper.createBubble(handle));
-        mBubbleEntry2User11 = BubblesManager.notifToBubbleEntry(
-                mNotificationTestHelper.createBubble(handle));
-
-        mZenModeConfig.suppressedVisualEffects = 0;
-        when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);
-
-        mSysUiState = new SysUiState();
-        mSysUiState.addCallback(sysUiFlags -> {
-            mSysUiStateBubblesManageMenuExpanded =
-                    (sysUiFlags
-                            & QuickStepContract.SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED) != 0;
-            mSysUiStateBubblesExpanded =
-                    (sysUiFlags & QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED) != 0;
-        });
-
-        mPositioner = new TestableBubblePositioner(mContext, mWindowManager);
-        mPositioner.setMaxBubbles(5);
-        mBubbleData = new BubbleData(mContext, mBubbleLogger, mPositioner, syncExecutor);
-
-        TestableNotificationInterruptStateProviderImpl interruptionStateProvider =
-                new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(),
-                        mock(PowerManager.class),
-                        mock(IDreamManager.class),
-                        mock(AmbientDisplayConfiguration.class),
-                        mock(NotificationFilter.class),
-                        mock(StatusBarStateController.class),
-                        mock(BatteryController.class),
-                        mock(HeadsUpManager.class),
-                        mock(NotificationInterruptLogger.class),
-                        mock(Handler.class),
-                        mock(NotifPipelineFlags.class),
-                        mock(KeyguardNotificationVisibilityProvider.class)
-                );
-        when(mNotifPipelineFlags.isNewPipelineEnabled()).thenReturn(true);
-        when(mShellTaskOrganizer.getExecutor()).thenReturn(syncExecutor);
-        mBubbleController = new TestableBubbleController(
-                mContext,
-                mBubbleData,
-                mFloatingContentCoordinator,
-                mDataRepository,
-                mStatusBarService,
-                mWindowManager,
-                mWindowManagerShellWrapper,
-                mock(UserManager.class),
-                mLauncherApps,
-                mBubbleLogger,
-                mTaskStackListener,
-                mShellTaskOrganizer,
-                mPositioner,
-                mock(DisplayController.class),
-                mOneHandedOptional,
-                mock(DragAndDropController.class),
-                syncExecutor,
-                mock(Handler.class),
-                mTaskViewTransitions,
-                mock(SyncTransactionQueue.class));
-        mBubbleController.setExpandListener(mBubbleExpandListener);
-        spyOn(mBubbleController);
-
-        mBubblesManager = new BubblesManager(
-                mContext,
-                mBubbleController.asBubbles(),
-                mNotificationShadeWindowController,
-                mock(KeyguardStateController.class),
-                mShadeController,
-                mConfigurationController,
-                mStatusBarService,
-                mock(INotificationManager.class),
-                mVisibilityProvider,
-                interruptionStateProvider,
-                mZenModeController,
-                mLockscreenUserManager,
-                mNotificationGroupManager,
-                mNotificationEntryManager,
-                mCommonNotifCollection,
-                mNotifPipeline,
-                mSysUiState,
-                mNotifPipelineFlags,
-                mDumpManager,
-                syncExecutor);
-        mBubblesManager.addNotifCallback(mNotifCallback);
-
-        // Get a reference to the BubbleController's entry listener
-        verify(mNotifPipeline, atLeastOnce())
-                .addCollectionListener(mNotifListenerCaptor.capture());
-        mEntryListener = mNotifListenerCaptor.getValue();
-    }
-
-    @Test
-    public void testAddBubble() {
-        mBubbleController.updateBubble(mBubbleEntry);
-        assertTrue(mBubbleController.hasBubbles());
-        assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
-    }
-
-    @Test
-    public void testHasBubbles() {
-        assertFalse(mBubbleController.hasBubbles());
-        mBubbleController.updateBubble(mBubbleEntry);
-        assertTrue(mBubbleController.hasBubbles());
-        assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
-    }
-
-    @Test
-    public void testRemoveBubble() {
-        mBubbleController.updateBubble(mBubbleEntry);
-        assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow.getKey()));
-        assertTrue(mBubbleController.hasBubbles());
-        verify(mNotifCallback, times(1)).invalidateNotifications(anyString());
-
-        mBubbleController.removeBubble(
-                mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
-        assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getKey()));
-        verify(mNotifCallback, times(2)).invalidateNotifications(anyString());
-
-        assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
-    }
-
-    @Test
-    public void testRemoveBubble_withDismissedNotif_inOverflow() {
-        mEntryListener.onEntryAdded(mRow);
-        mBubbleController.updateBubble(mBubbleEntry);
-
-        assertTrue(mBubbleController.hasBubbles());
-        assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
-
-        // Make it look like dismissed notif
-        mBubbleData.getBubbleInStackWithKey(mRow.getKey()).setSuppressNotification(true);
-
-        // Now remove the bubble
-        mBubbleController.removeBubble(
-                mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
-        assertTrue(mBubbleData.hasOverflowBubbleWithKey(mRow.getKey()));
-
-        // We don't remove the notification since the bubble is still in overflow.
-        verify(mNotifCallback, never()).removeNotification(eq(mRow), any(), anyInt());
-        assertFalse(mBubbleController.hasBubbles());
-    }
-
-    @Test
-    public void testRemoveBubble_withDismissedNotif_notInOverflow() {
-        mEntryListener.onEntryAdded(mRow);
-        mBubbleController.updateBubble(mBubbleEntry);
-        when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow);
-
-        assertTrue(mBubbleController.hasBubbles());
-        assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
-
-        // Make it look like dismissed notif
-        mBubbleData.getBubbleInStackWithKey(mRow.getKey()).setSuppressNotification(true);
-
-        // Now remove the bubble
-        mBubbleController.removeBubble(
-                mRow.getKey(), Bubbles.DISMISS_NOTIF_CANCEL);
-        assertFalse(mBubbleData.hasOverflowBubbleWithKey(mRow.getKey()));
-
-        // Since the notif is dismissed and not in overflow, once the bubble is removed,
-        // removeNotification gets called to really remove the notif
-        verify(mNotifCallback, times(1)).removeNotification(eq(mRow),
-                any(), anyInt());
-        assertFalse(mBubbleController.hasBubbles());
-    }
-
-    @Test
-    public void testDismissStack() {
-        mBubbleController.updateBubble(mBubbleEntry);
-        verify(mNotifCallback, times(1)).invalidateNotifications(anyString());
-        assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow.getKey()));
-        mBubbleController.updateBubble(mBubbleEntry2);
-        verify(mNotifCallback, times(2)).invalidateNotifications(anyString());
-        assertNotNull(mBubbleData.getBubbleInStackWithKey(mRow2.getKey()));
-        assertTrue(mBubbleController.hasBubbles());
-
-        mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE);
-        verify(mNotifCallback, times(3)).invalidateNotifications(anyString());
-        assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getKey()));
-        assertNull(mBubbleData.getBubbleInStackWithKey(mRow2.getKey()));
-
-        assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
-    }
-
-    @Test
-    public void testExpandCollapseStack() {
-        assertStackCollapsed();
-
-        // Mark it as a bubble and add it explicitly
-        mEntryListener.onEntryAdded(mRow);
-        mBubbleController.updateBubble(mBubbleEntry);
-
-        // We should have bubbles & their notifs should not be suppressed
-        assertTrue(mBubbleController.hasBubbles());
-        assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
-
-        // Expand the stack
-        mBubbleData.setExpanded(true);
-        assertStackExpanded();
-        verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey());
-        assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
-
-        // Make sure the notif is suppressed
-        assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
-
-        // Collapse
-        mBubbleController.collapseStack();
-        verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getKey());
-        assertStackCollapsed();
-        assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
-    }
-
-    @Test
-    @Ignore("Currently broken.")
-    public void testCollapseAfterChangingExpandedBubble() {
-        // Mark it as a bubble and add it explicitly
-        mEntryListener.onEntryAdded(mRow);
-        mEntryListener.onEntryAdded(mRow2);
-        mBubbleController.updateBubble(mBubbleEntry);
-        mBubbleController.updateBubble(mBubbleEntry2);
-
-        // We should have bubbles & their notifs should not be suppressed
-        assertTrue(mBubbleController.hasBubbles());
-        assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
-        assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry2);
-
-        // Expand
-        BubbleStackView stackView = mBubbleController.getStackView();
-        mBubbleData.setExpanded(true);
-        assertStackExpanded();
-        verify(mBubbleExpandListener, atLeastOnce()).onBubbleExpandChanged(
-                true, mRow2.getKey());
-        assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
-
-        // Last added is the one that is expanded
-        assertEquals(mRow2.getKey(), mBubbleData.getSelectedBubble().getKey());
-        assertBubbleNotificationSuppressedFromShade(mBubbleEntry2);
-
-        // Switch which bubble is expanded
-        mBubbleData.setSelectedBubble(mBubbleData.getBubbleInStackWithKey(
-                mRow.getKey()));
-        mBubbleData.setExpanded(true);
-        assertEquals(mRow.getKey(), mBubbleData.getBubbleInStackWithKey(
-                stackView.getExpandedBubble().getKey()).getKey());
-        assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
-
-        // collapse for previous bubble
-        verify(mBubbleExpandListener, atLeastOnce()).onBubbleExpandChanged(
-                false, mRow2.getKey());
-        // expand for selected bubble
-        verify(mBubbleExpandListener, atLeastOnce()).onBubbleExpandChanged(
-                true, mRow.getKey());
-
-
-        // Collapse
-        mBubbleController.collapseStack();
-        assertStackCollapsed();
-        assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
-    }
-
-    @Test
-    public void testExpansionRemovesShowInShadeAndDot() {
-        // Mark it as a bubble and add it explicitly
-        mEntryListener.onEntryAdded(mRow);
-        mBubbleController.updateBubble(mBubbleEntry);
-
-        // We should have bubbles & their notifs should not be suppressed
-        assertTrue(mBubbleController.hasBubbles());
-        assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
-
-        mTestableLooper.processAllMessages();
-        assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
-
-        // Expand
-        mBubbleData.setExpanded(true);
-        assertStackExpanded();
-        verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey());
-        assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
-
-        // Notif is suppressed after expansion
-        assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
-        // Notif shouldn't show dot after expansion
-        assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
-    }
-
-    @Test
-    public void testUpdateWhileExpanded_DoesntChangeShowInShadeAndDot() {
-        // Mark it as a bubble and add it explicitly
-        mEntryListener.onEntryAdded(mRow);
-        mBubbleController.updateBubble(mBubbleEntry);
-
-        // We should have bubbles & their notifs should not be suppressed
-        assertTrue(mBubbleController.hasBubbles());
-        assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
-
-        mTestableLooper.processAllMessages();
-        assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
-
-        // Expand
-        mBubbleData.setExpanded(true);
-        assertStackExpanded();
-        verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey());
-        assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
-
-        // Notif is suppressed after expansion
-        assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
-        // Notif shouldn't show dot after expansion
-        assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
-
-        // Send update
-        mEntryListener.onEntryUpdated(mRow);
-
-        // Nothing should have changed
-        // Notif is suppressed after expansion
-        assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
-        // Notif shouldn't show dot after expansion
-        assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
-    }
-
-    @Test
-    public void testRemoveLastExpanded_collapses() {
-        // Mark it as a bubble and add it explicitly
-        mEntryListener.onEntryAdded(mRow);
-        mEntryListener.onEntryAdded(mRow2);
-        mBubbleController.updateBubble(mBubbleEntry);
-        mBubbleController.updateBubble(mBubbleEntry2);
-
-        // Expand
-        BubbleStackView stackView = mBubbleController.getStackView();
-        mBubbleData.setExpanded(true);
-
-        assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
-
-        assertStackExpanded();
-        verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getKey());
-
-        // Last added is the one that is expanded
-        assertEquals(mRow2.getKey(), mBubbleData.getBubbleInStackWithKey(
-                stackView.getExpandedBubble().getKey()).getKey());
-        assertBubbleNotificationSuppressedFromShade(mBubbleEntry2);
-
-        // Dismiss currently expanded
-        mBubbleController.removeBubble(
-                mBubbleData.getBubbleInStackWithKey(
-                        stackView.getExpandedBubble().getKey()).getKey(),
-                Bubbles.DISMISS_USER_GESTURE);
-        verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getKey());
-
-        // Make sure first bubble is selected
-        assertEquals(mRow.getKey(), mBubbleData.getBubbleInStackWithKey(
-                stackView.getExpandedBubble().getKey()).getKey());
-        verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey());
-
-        // Dismiss that one
-        mBubbleController.removeBubble(
-                mBubbleData.getBubbleInStackWithKey(
-                        stackView.getExpandedBubble().getKey()).getKey(),
-                Bubbles.DISMISS_USER_GESTURE);
-
-        // We should be collapsed
-        verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getKey());
-        assertFalse(mBubbleController.hasBubbles());
-        assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
-    }
-
-    @Test
-    public void testRemoveLastExpandedEmptyOverflow_collapses() {
-        // Mark it as a bubble and add it explicitly
-        mEntryListener.onEntryAdded(mRow);
-        mBubbleController.updateBubble(mBubbleEntry);
-
-        // Expand
-        BubbleStackView stackView = mBubbleController.getStackView();
-        mBubbleData.setExpanded(true);
-
-        assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
-        assertStackExpanded();
-        verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey());
-
-        // Block the bubble so it won't be in the overflow
-        mBubbleController.removeBubble(
-                mBubbleData.getBubbleInStackWithKey(
-                        stackView.getExpandedBubble().getKey()).getKey(),
-                Bubbles.DISMISS_BLOCKED);
-
-        verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getKey());
-
-        // We should be collapsed
-        verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getKey());
-        assertFalse(mBubbleController.hasBubbles());
-        assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
-    }
-
-
-    @Test
-    public void testAutoExpand_fails_noFlag() {
-        assertStackCollapsed();
-        setMetadataFlags(mRow,
-                Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE, false /* enableFlag */);
-
-        // Add the auto expand bubble
-        mEntryListener.onEntryAdded(mRow);
-        mBubbleController.updateBubble(mBubbleEntry);
-
-        // Expansion shouldn't change
-        verify(mBubbleExpandListener, never()).onBubbleExpandChanged(false /* expanded */,
-                mRow.getKey());
-        assertStackCollapsed();
-        assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
-    }
-
-    @Test
-    public void testAutoExpand_succeeds_withFlag() {
-        setMetadataFlags(mRow,
-                Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE, true /* enableFlag */);
-
-        // Add the auto expand bubble
-        mEntryListener.onEntryAdded(mRow);
-        mBubbleController.updateBubble(mBubbleEntry);
-
-        // Expansion should change
-        verify(mBubbleExpandListener).onBubbleExpandChanged(true /* expanded */,
-                mRow.getKey());
-        assertStackExpanded();
-        assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
-    }
-
-    @Test
-    public void testSuppressNotif_onInitialNotif() {
-        setMetadataFlags(mRow,
-                Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, true /* enableFlag */);
-
-        // Add the suppress notif bubble
-        mEntryListener.onEntryAdded(mRow);
-        mBubbleController.updateBubble(mBubbleEntry);
-
-        // Notif should be suppressed because we were foreground
-        assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
-        // Dot + flyout is hidden because notif is suppressed
-        assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
-        assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showFlyout());
-        assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
-    }
-
-    @Test
-    public void testSuppressNotif_onUpdateNotif() {
-        mBubbleController.updateBubble(mBubbleEntry);
-
-        // Should not be suppressed
-        assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
-        // Should show dot
-        assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
-
-        // Update to suppress notif
-        setMetadataFlags(mRow,
-                Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, true /* enableFlag */);
-        mBubbleController.updateBubble(mBubbleEntry);
-
-        // Notif should be suppressed
-        assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
-        // Dot + flyout is hidden because notif is suppressed
-        assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
-        assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showFlyout());
-        assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
-    }
-
-    @Test
-    public void testMarkNewNotificationAsShowInShade() {
-        mEntryListener.onEntryAdded(mRow);
-        assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
-
-        mTestableLooper.processAllMessages();
-        assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
-    }
-
-    @Test
-    public void testAddNotif_notBubble() {
-        mEntryListener.onEntryAdded(mNonBubbleNotifRow.getEntry());
-        mEntryListener.onEntryUpdated(mNonBubbleNotifRow.getEntry());
-
-        assertThat(mBubbleController.hasBubbles()).isFalse();
-    }
-
-    @Test
-    public void testDeleteIntent_removeBubble_aged() throws PendingIntent.CanceledException {
-        mBubbleController.updateBubble(mBubbleEntry);
-        mBubbleController.removeBubble(mRow.getKey(), Bubbles.DISMISS_AGED);
-        verify(mDeleteIntent, never()).send();
-    }
-
-    @Test
-    public void testDeleteIntent_removeBubble_user() throws PendingIntent.CanceledException {
-        mBubbleController.updateBubble(mBubbleEntry);
-        mBubbleController.removeBubble(
-                mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
-        verify(mDeleteIntent, times(1)).send();
-    }
-
-    @Test
-    public void testDeleteIntent_dismissStack() throws PendingIntent.CanceledException {
-        mBubbleController.updateBubble(mBubbleEntry);
-        mBubbleController.updateBubble(mBubbleEntry2);
-        mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE);
-        verify(mDeleteIntent, times(2)).send();
-    }
-
-    @Test
-    public void testRemoveBubble_noLongerBubbleAfterUpdate()
-            throws PendingIntent.CanceledException {
-        mBubbleController.updateBubble(mBubbleEntry);
-        assertTrue(mBubbleController.hasBubbles());
-
-        mRow.getSbn().getNotification().flags &= ~FLAG_BUBBLE;
-        NotificationListenerService.Ranking ranking = new RankingBuilder(
-                mRow.getRanking()).setCanBubble(false).build();
-        mRow.setRanking(ranking);
-        mEntryListener.onEntryUpdated(mRow);
-
-        assertFalse(mBubbleController.hasBubbles());
-        verify(mDeleteIntent, never()).send();
-    }
-
-    @Test
-    public void testRemoveBubble_entryListenerRemove() {
-        mEntryListener.onEntryAdded(mRow);
-        mBubbleController.updateBubble(mBubbleEntry);
-
-        assertTrue(mBubbleController.hasBubbles());
-
-        // Removes the notification
-        mEntryListener.onEntryRemoved(mRow, REASON_APP_CANCEL);
-        assertFalse(mBubbleController.hasBubbles());
-    }
-
-    @Test
-    public void removeBubble_intercepted() {
-        mEntryListener.onEntryAdded(mRow);
-        mBubbleController.updateBubble(mBubbleEntry);
-
-        assertTrue(mBubbleController.hasBubbles());
-        assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
-
-        boolean intercepted = mBubblesManager.handleDismissalInterception(mRow);
-
-        // Intercept!
-        assertTrue(intercepted);
-        // Should update show in shade state
-        assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
-    }
-
-    @Test
-    public void removeBubble_dismissIntoOverflow_intercepted() {
-        mEntryListener.onEntryAdded(mRow);
-        mBubbleController.updateBubble(mBubbleEntry);
-
-        assertTrue(mBubbleController.hasBubbles());
-        assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
-
-        // Dismiss the bubble
-        mBubbleController.removeBubble(mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
-        assertFalse(mBubbleController.hasBubbles());
-
-        // Dismiss the notification
-        boolean intercepted = mBubblesManager.handleDismissalInterception(mRow);
-
-        // Intercept dismissal since bubble is going into overflow
-        assertTrue(intercepted);
-    }
-
-    @Test
-    public void removeBubble_notIntercepted() {
-        mEntryListener.onEntryAdded(mRow);
-        mBubbleController.updateBubble(mBubbleEntry);
-
-        assertTrue(mBubbleController.hasBubbles());
-        assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
-
-        // Dismiss the bubble
-        mBubbleController.removeBubble(mRow.getKey(), Bubbles.DISMISS_NOTIF_CANCEL);
-        assertFalse(mBubbleController.hasBubbles());
-
-        // Dismiss the notification
-        boolean intercepted = mBubblesManager.handleDismissalInterception(mRow);
-
-        // Not a bubble anymore so we don't intercept dismissal.
-        assertFalse(intercepted);
-    }
-
-    @Test
-    public void testNotifyShadeSuppressionChange_notificationDismiss() {
-        mEntryListener.onEntryAdded(mRow);
-
-        assertTrue(mBubbleController.hasBubbles());
-        assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
-
-        mBubblesManager.handleDismissalInterception(mRow);
-
-        // Should update show in shade state
-        assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
-
-        // Should notify delegate that shade state changed
-        verify(mBubbleController).onBubbleMetadataFlagChanged(
-                mBubbleData.getBubbleInStackWithKey(mRow.getKey()));
-    }
-
-    @Test
-    public void testNotifyShadeSuppressionChange_bubbleExpanded() {
-        mEntryListener.onEntryAdded(mRow);
-
-        assertTrue(mBubbleController.hasBubbles());
-        assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
-
-        mBubbleData.setExpanded(true);
-
-        // Once a bubble is expanded the notif is suppressed
-        assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
-
-        // Should notify delegate that shade state changed
-        verify(mBubbleController).onBubbleMetadataFlagChanged(
-                mBubbleData.getBubbleInStackWithKey(mRow.getKey()));
-    }
-
-    @Test
-    public void testBubbleSummaryDismissal_suppressesSummaryAndBubbleFromShade() throws Exception {
-        // GIVEN a group summary with a bubble child
-        ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0);
-        ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
-        mEntryListener.onEntryAdded(groupedBubble.getEntry());
-        when(mCommonNotifCollection.getEntry(groupedBubble.getEntry().getKey()))
-                .thenReturn(groupedBubble.getEntry());
-        groupSummary.addChildNotification(groupedBubble);
-        assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
-
-        // WHEN the summary is dismissed
-        mBubblesManager.handleDismissalInterception(groupSummary.getEntry());
-
-        // THEN the summary and bubbled child are suppressed from the shade
-        assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
-                groupedBubble.getEntry().getKey(),
-                groupedBubble.getEntry().getSbn().getGroupKey()));
-        assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade(
-                groupedBubble.getEntry().getKey(),
-                groupedBubble.getEntry().getSbn().getGroupKey()));
-        assertTrue(mBubbleData.isSummarySuppressed(groupSummary.getEntry().getSbn().getGroupKey()));
-    }
-
-    @Test
-    public void testAppRemovesSummary_removesAllBubbleChildren() throws Exception {
-        // GIVEN a group summary with a bubble child
-        ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0);
-        ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
-        mEntryListener.onEntryAdded(groupedBubble.getEntry());
-        when(mCommonNotifCollection.getEntry(groupedBubble.getEntry().getKey()))
-                .thenReturn(groupedBubble.getEntry());
-        groupSummary.addChildNotification(groupedBubble);
-        assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
-
-        // GIVEN the summary is dismissed
-        mBubblesManager.handleDismissalInterception(groupSummary.getEntry());
-
-        // WHEN the summary is cancelled by the app
-        mEntryListener.onEntryRemoved(groupSummary.getEntry(), REASON_APP_CANCEL);
-
-        // THEN the summary and its children are removed from bubble data
-        assertFalse(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
-        assertFalse(mBubbleData.isSummarySuppressed(
-                groupSummary.getEntry().getSbn().getGroupKey()));
-    }
-
-    @Test
-    public void testSummaryDismissalMarksBubblesHiddenFromShadeAndDismissesNonBubbledChildren()
-            throws Exception {
-        // GIVEN a group summary with two (non-bubble) children and one bubble child
-        ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(2);
-        ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
-        mEntryListener.onEntryAdded(groupedBubble.getEntry());
-        when(mCommonNotifCollection.getEntry(groupedBubble.getEntry().getKey()))
-                .thenReturn(groupedBubble.getEntry());
-        groupSummary.addChildNotification(groupedBubble);
-
-        // WHEN the summary is dismissed
-        mBubblesManager.handleDismissalInterception(groupSummary.getEntry());
-
-        // THEN only the NON-bubble children are dismissed
-        List<ExpandableNotificationRow> childrenRows = groupSummary.getAttachedChildren();
-        verify(mNotifCallback, times(1)).removeNotification(
-                eq(childrenRows.get(0).getEntry()), any(), eq(REASON_GROUP_SUMMARY_CANCELED));
-        verify(mNotifCallback, times(1)).removeNotification(
-                eq(childrenRows.get(1).getEntry()), any(), eq(REASON_GROUP_SUMMARY_CANCELED));
-        verify(mNotifCallback, never()).removeNotification(eq(groupedBubble.getEntry()),
-                any(), anyInt());
-
-        // THEN the bubble child still exists as a bubble and is suppressed from the shade
-        assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
-        assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
-                groupedBubble.getEntry().getKey(),
-                groupedBubble.getEntry().getSbn().getGroupKey()));
-        assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade(
-                groupedBubble.getEntry().getKey(),
-                groupedBubble.getEntry().getSbn().getGroupKey()));
-
-        // THEN the summary is also suppressed from the shade
-        assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
-                groupSummary.getEntry().getKey(),
-                groupSummary.getEntry().getSbn().getGroupKey()));
-        assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade(
-                groupSummary.getEntry().getKey(),
-                groupSummary.getEntry().getSbn().getGroupKey()));
-    }
-
-
-    /**
-     * Verifies that when the user changes, the bubbles in the overflow list is cleared. Doesn't
-     * test the loading from the repository which would be a nice thing to add.
-     */
-    @Test
-    public void testOnUserChanged_overflowState() {
-        int firstUserId = mBubbleEntry.getStatusBarNotification().getUser().getIdentifier();
-        int secondUserId = mBubbleEntryUser11.getStatusBarNotification().getUser().getIdentifier();
-
-        mBubbleController.updateBubble(mBubbleEntry);
-        mBubbleController.updateBubble(mBubbleEntry2);
-        assertTrue(mBubbleController.hasBubbles());
-        mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE);
-
-        // Verify these are in the overflow
-        assertThat(mBubbleData.getOverflowBubbleWithKey(mBubbleEntry.getKey())).isNotNull();
-        assertThat(mBubbleData.getOverflowBubbleWithKey(mBubbleEntry2.getKey())).isNotNull();
-
-        // Switch users
-        mBubbleController.onUserChanged(secondUserId);
-        assertThat(mBubbleData.getOverflowBubbles()).isEmpty();
-
-        // Give this user some bubbles
-        mBubbleController.updateBubble(mBubbleEntryUser11);
-        mBubbleController.updateBubble(mBubbleEntry2User11);
-        assertTrue(mBubbleController.hasBubbles());
-        mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE);
-
-        // Verify these are in the overflow
-        assertThat(mBubbleData.getOverflowBubbleWithKey(mBubbleEntryUser11.getKey())).isNotNull();
-        assertThat(mBubbleData.getOverflowBubbleWithKey(mBubbleEntry2User11.getKey())).isNotNull();
-
-        // Would have loaded bubbles twice because of user switch
-        verify(mDataRepository, times(2)).loadBubbles(anyInt(), any());
-    }
-
-    /**
-     * Verifies we only load the overflow data once.
-     */
-    @Test
-    public void testOverflowLoadedOnce() {
-        // XXX
-        when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow);
-        when(mCommonNotifCollection.getEntry(mRow2.getKey())).thenReturn(mRow2);
-
-        mEntryListener.onEntryAdded(mRow);
-        mEntryListener.onEntryAdded(mRow2);
-        mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE);
-        assertThat(mBubbleData.getOverflowBubbles()).isNotEmpty();
-
-        mEntryListener.onEntryRemoved(mRow, REASON_APP_CANCEL);
-        mEntryListener.onEntryRemoved(mRow2, REASON_APP_CANCEL);
-        assertThat(mBubbleData.getOverflowBubbles()).isEmpty();
-
-        verify(mDataRepository, times(1)).loadBubbles(anyInt(), any());
-    }
-
-    /**
-     * Verifies that shortcut deletions triggers that bubble being removed from XML.
-     */
-    @Test
-    public void testDeleteShortcutsDeletesXml() throws Exception {
-        ExpandableNotificationRow row = mNotificationTestHelper.createShortcutBubble("shortcutId");
-        BubbleEntry shortcutBubbleEntry = BubblesManager.notifToBubbleEntry(row.getEntry());
-        mBubbleController.updateBubble(shortcutBubbleEntry);
-
-        mBubbleData.dismissBubbleWithKey(shortcutBubbleEntry.getKey(),
-                Bubbles.DISMISS_SHORTCUT_REMOVED);
-
-        verify(mDataRepository, atLeastOnce()).removeBubbles(anyInt(), mBubbleListCaptor.capture());
-        assertThat(mBubbleListCaptor.getValue().get(0).getKey()).isEqualTo(
-                shortcutBubbleEntry.getKey());
-    }
-
-    @Test
-    public void testShowManageMenuChangesSysuiState() {
-        mBubbleController.updateBubble(mBubbleEntry);
-        assertTrue(mBubbleController.hasBubbles());
-
-        // Expand the stack
-        BubbleStackView stackView = mBubbleController.getStackView();
-        mBubbleData.setExpanded(true);
-        assertStackExpanded();
-        assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
-
-        // Show the menu
-        stackView.showManageMenu(true);
-        assertSysuiStates(true /* stackExpanded */, true /* mangeMenuExpanded */);
-    }
-
-    @Test
-    public void testHideManageMenuChangesSysuiState() {
-        mBubbleController.updateBubble(mBubbleEntry);
-        assertTrue(mBubbleController.hasBubbles());
-
-        // Expand the stack
-        BubbleStackView stackView = mBubbleController.getStackView();
-        mBubbleData.setExpanded(true);
-        assertStackExpanded();
-        assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
-
-        // Show the menu
-        stackView.showManageMenu(true);
-        assertSysuiStates(true /* stackExpanded */, true /* mangeMenuExpanded */);
-
-        // Hide the menu
-        stackView.showManageMenu(false);
-        assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
-    }
-
-    @Test
-    public void testCollapseBubbleManageMenuChangesSysuiState() {
-        mBubbleController.updateBubble(mBubbleEntry);
-        assertTrue(mBubbleController.hasBubbles());
-
-        // Expand the stack
-        BubbleStackView stackView = mBubbleController.getStackView();
-        mBubbleData.setExpanded(true);
-        assertStackExpanded();
-        assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
-
-        // Show the menu
-        stackView.showManageMenu(true);
-        assertSysuiStates(true /* stackExpanded */, true /* mangeMenuExpanded */);
-
-        // Collapse the stack
-        mBubbleData.setExpanded(false);
-
-        assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
-    }
-
-    @Test
-    public void testNotificationChannelModified_channelUpdated_removesOverflowBubble()
-            throws Exception {
-        // Setup
-        ExpandableNotificationRow row = mNotificationTestHelper.createShortcutBubble("shortcutId");
-        NotificationEntry entry = row.getEntry();
-        entry.getChannel().setConversationId(
-                row.getEntry().getChannel().getParentChannelId(),
-                "shortcutId");
-        mBubbleController.updateBubble(BubblesManager.notifToBubbleEntry(row.getEntry()));
-        assertTrue(mBubbleController.hasBubbles());
-
-        // Overflow it
-        mBubbleData.dismissBubbleWithKey(entry.getKey(),
-                Bubbles.DISMISS_USER_GESTURE);
-        assertThat(mBubbleData.hasOverflowBubbleWithKey(entry.getKey())).isTrue();
-
-        // Test
-        entry.getChannel().setDeleted(true);
-        mBubbleController.onNotificationChannelModified(entry.getSbn().getPackageName(),
-                entry.getSbn().getUser(),
-                entry.getChannel(),
-                NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
-        assertThat(mBubbleData.hasOverflowBubbleWithKey(entry.getKey())).isFalse();
-    }
-
-    @Test
-    public void testNotificationChannelModified_channelDeleted_removesOverflowBubble()
-            throws Exception {
-        // Setup
-        ExpandableNotificationRow row = mNotificationTestHelper.createShortcutBubble("shortcutId");
-        NotificationEntry entry = row.getEntry();
-        entry.getChannel().setConversationId(
-                row.getEntry().getChannel().getParentChannelId(),
-                "shortcutId");
-        mBubbleController.updateBubble(BubblesManager.notifToBubbleEntry(row.getEntry()));
-        assertTrue(mBubbleController.hasBubbles());
-
-        // Overflow it
-        mBubbleData.dismissBubbleWithKey(entry.getKey(),
-                Bubbles.DISMISS_USER_GESTURE);
-        assertThat(mBubbleData.hasOverflowBubbleWithKey(entry.getKey())).isTrue();
-
-        // Test
-        entry.getChannel().setDeleted(true);
-        mBubbleController.onNotificationChannelModified(entry.getSbn().getPackageName(),
-                entry.getSbn().getUser(),
-                entry.getChannel(),
-                NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
-        assertThat(mBubbleData.hasOverflowBubbleWithKey(entry.getKey())).isFalse();
-    }
-
-    @Test
-    public void testStackViewOnBackPressed_updatesBubbleDataExpandState() {
-        mBubbleController.updateBubble(mBubbleEntry);
-
-        // Expand the stack
-        mBubbleData.setExpanded(true);
-        assertStackExpanded();
-
-        // Hit back
-        BubbleStackView stackView = mBubbleController.getStackView();
-        stackView.onBackPressed();
-
-        // Make sure we're collapsed
-        assertStackCollapsed();
-    }
-
-
-    @Test
-    public void testRegisterUnregisterBroadcastListener() {
-        spyOn(mContext);
-        mBubbleController.updateBubble(mBubbleEntry);
-        verify(mContext).registerReceiver(mBroadcastReceiverArgumentCaptor.capture(),
-                mFilterArgumentCaptor.capture());
-        assertThat(mFilterArgumentCaptor.getValue().getAction(0)).isEqualTo(
-                Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
-        assertThat(mFilterArgumentCaptor.getValue().getAction(1)).isEqualTo(
-                Intent.ACTION_SCREEN_OFF);
-
-        mBubbleData.dismissBubbleWithKey(mBubbleEntry.getKey(), REASON_APP_CANCEL);
-        // TODO: not certain why this isn't called normally when tests are run, perhaps because
-        // it's after an animation in BSV. This calls BubbleController#removeFromWindowManagerMaybe
-        mBubbleController.onAllBubblesAnimatedOut();
-
-        verify(mContext).unregisterReceiver(eq(mBroadcastReceiverArgumentCaptor.getValue()));
-    }
-
-    @Test
-    public void testBroadcastReceiverCloseDialogs_notGestureNav() {
-        spyOn(mContext);
-        mBubbleController.updateBubble(mBubbleEntry);
-        mBubbleData.setExpanded(true);
-        verify(mContext).registerReceiver(mBroadcastReceiverArgumentCaptor.capture(),
-                mFilterArgumentCaptor.capture());
-        Intent i = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
-        mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, i);
-
-        assertStackExpanded();
-    }
-
-    @Test
-    public void testBroadcastReceiverCloseDialogs_reasonGestureNav() {
-        spyOn(mContext);
-        mBubbleController.updateBubble(mBubbleEntry);
-        mBubbleData.setExpanded(true);
-
-        verify(mContext).registerReceiver(mBroadcastReceiverArgumentCaptor.capture(),
-                mFilterArgumentCaptor.capture());
-        Intent i = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
-        i.putExtra("reason", "gestureNav");
-        mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, i);
-        assertStackCollapsed();
-    }
-
-    @Test
-    public void testBroadcastReceiver_screenOff() {
-        spyOn(mContext);
-        mBubbleController.updateBubble(mBubbleEntry);
-        mBubbleData.setExpanded(true);
-
-        verify(mContext).registerReceiver(mBroadcastReceiverArgumentCaptor.capture(),
-                mFilterArgumentCaptor.capture());
-
-        Intent i = new Intent(Intent.ACTION_SCREEN_OFF);
-        mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, i);
-        assertStackCollapsed();
-    }
-
-    @Test
-    public void testOnStatusBarStateChanged() {
-        mBubbleController.updateBubble(mBubbleEntry);
-        mBubbleData.setExpanded(true);
-        assertStackExpanded();
-        BubbleStackView stackView = mBubbleController.getStackView();
-        assertThat(stackView.getVisibility()).isEqualTo(View.VISIBLE);
-
-        mBubbleController.onStatusBarStateChanged(false);
-
-        assertStackCollapsed();
-        assertThat(stackView.getVisibility()).isEqualTo(View.INVISIBLE);
-
-        mBubbleController.onStatusBarStateChanged(true);
-        assertThat(stackView.getVisibility()).isEqualTo(View.VISIBLE);
-    }
-
-    @Test
-    public void testSetShouldAutoExpand_notifiesFlagChanged() {
-        mBubbleController.updateBubble(mBubbleEntry);
-
-        assertTrue(mBubbleController.hasBubbles());
-        Bubble b = mBubbleData.getBubbleInStackWithKey(mBubbleEntry.getKey());
-        assertThat(b.shouldAutoExpand()).isFalse();
-
-        // Set it to the same thing
-        b.setShouldAutoExpand(false);
-
-        // Verify it doesn't notify
-        verify(mBubbleController, never()).onBubbleMetadataFlagChanged(any());
-
-        // Set it to something different
-        b.setShouldAutoExpand(true);
-        verify(mBubbleController).onBubbleMetadataFlagChanged(b);
-    }
-
-    @Test
-    public void testUpdateBubble_skipsDndSuppressListNotifs() {
-        mBubbleEntry = new BubbleEntry(mRow.getSbn(), mRow.getRanking(), mRow.isDismissable(),
-                mRow.shouldSuppressNotificationDot(), true /* DndSuppressNotifFromList */,
-                mRow.shouldSuppressPeek());
-        mBubbleEntry.getBubbleMetadata().setFlags(
-                Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE);
-
-        mBubbleController.updateBubble(mBubbleEntry);
-
-        Bubble b = mBubbleData.getPendingBubbleWithKey(mBubbleEntry.getKey());
-        assertThat(b.shouldAutoExpand()).isFalse();
-        assertThat(mBubbleData.getBubbleInStackWithKey(mBubbleEntry.getKey())).isNull();
-    }
-
-    @Test
-    public void testOnRankingUpdate_DndSuppressListNotif() {
-        // It's in the stack
-        mBubbleController.updateBubble(mBubbleEntry);
-        assertThat(mBubbleData.hasBubbleInStackWithKey(mBubbleEntry.getKey())).isTrue();
-
-        // Set current user profile
-        SparseArray<UserInfo> userInfos = new SparseArray<>();
-        userInfos.put(mBubbleEntry.getStatusBarNotification().getUser().getIdentifier(),
-                mock(UserInfo.class));
-        mBubbleController.onCurrentProfilesChanged(userInfos);
-
-        // Send ranking update that the notif is suppressed from the list.
-        HashMap<String, Pair<BubbleEntry, Boolean>> entryDataByKey = new HashMap<>();
-        mBubbleEntry = new BubbleEntry(mRow.getSbn(), mRow.getRanking(), mRow.isDismissable(),
-                mRow.shouldSuppressNotificationDot(), true /* DndSuppressNotifFromList */,
-                mRow.shouldSuppressPeek());
-        Pair<BubbleEntry, Boolean> pair = new Pair(mBubbleEntry, true);
-        entryDataByKey.put(mBubbleEntry.getKey(), pair);
-
-        NotificationListenerService.RankingMap rankingMap =
-                mock(NotificationListenerService.RankingMap.class);
-        when(rankingMap.getOrderedKeys()).thenReturn(new String[] { mBubbleEntry.getKey() });
-        mBubbleController.onRankingUpdated(rankingMap, entryDataByKey);
-
-        // Should no longer be in the stack
-        assertThat(mBubbleData.hasBubbleInStackWithKey(mBubbleEntry.getKey())).isFalse();
-    }
-
-    /**
-     * Sets the bubble metadata flags for this entry. These flags are normally set by
-     * NotificationManagerService when the notification is sent, however, these tests do not
-     * go through that path so we set them explicitly when testing.
-     */
-    private void setMetadataFlags(NotificationEntry entry, int flag, boolean enableFlag) {
-        Notification.BubbleMetadata bubbleMetadata =
-                entry.getSbn().getNotification().getBubbleMetadata();
-        int flags = bubbleMetadata.getFlags();
-        if (enableFlag) {
-            flags |= flag;
-        } else {
-            flags &= ~flag;
-        }
-        bubbleMetadata.setFlags(flags);
-    }
-
-    /**
-     * Asserts that the bubble stack is expanded and also validates the cached state is updated.
-     */
-    private void assertStackExpanded() {
-        assertTrue(mBubbleController.isStackExpanded());
-        assertTrue(mBubbleController.getImplCachedState().isStackExpanded());
-    }
-
-    /**
-     * Asserts that the bubble stack is collapsed and also validates the cached state is updated.
-     */
-    private void assertStackCollapsed() {
-        assertFalse(mBubbleController.isStackExpanded());
-        assertFalse(mBubbleController.getImplCachedState().isStackExpanded());
-    }
-
-    /**
-     * Asserts that a bubble notification is suppressed from the shade and also validates the cached
-     * state is updated.
-     */
-    private void assertBubbleNotificationSuppressedFromShade(BubbleEntry entry) {
-        assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
-                entry.getKey(), entry.getGroupKey()));
-        assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade(
-                entry.getKey(), entry.getGroupKey()));
-    }
-
-    /**
-     * Asserts that a bubble notification is not suppressed from the shade and also validates the
-     * cached state is updated.
-     */
-    private void assertBubbleNotificationNotSuppressedFromShade(BubbleEntry entry) {
-        assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
-                entry.getKey(), entry.getGroupKey()));
-        assertFalse(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade(
-                entry.getKey(), entry.getGroupKey()));
-    }
-
-    /**
-     * Asserts that the system ui states associated to bubbles are in the correct state.
-     */
-    private void assertSysuiStates(boolean stackExpanded, boolean manageMenuExpanded) {
-        assertThat(mSysUiStateBubblesExpanded).isEqualTo(stackExpanded);
-        assertThat(mSysUiStateBubblesManageMenuExpanded).isEqualTo(manageMenuExpanded);
-    }
-}