diff options
4 files changed, 167 insertions, 53 deletions
diff --git a/services/core/java/com/android/server/notification/GroupHelper.java b/services/core/java/com/android/server/notification/GroupHelper.java index 02f817e9cd44..6e5308e56aa8 100644 --- a/services/core/java/com/android/server/notification/GroupHelper.java +++ b/services/core/java/com/android/server/notification/GroupHelper.java @@ -35,6 +35,7 @@ import android.app.ActivityManager; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; +import android.app.PendingIntent; import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; @@ -130,8 +131,10 @@ public class GroupHelper { mUngroupedAbuseNotifications = new ArrayMap<>(); // Contains the list of group summaries that were canceled when "singleton groups" were - // force grouped. Used to remove the original group's children when an app cancels the - // already removed summary. Key is userId|packageName|g:OriginalGroupName + // force grouped. Key is userId|packageName|g:OriginalGroupName. Used to: + // 1) remove the original group's children when an app cancels the already removed summary. + // 2) perform the same side effects that would happen if the group is removed because + // all its force-regrouped children are removed (e.g. firing its deleteIntent). @GuardedBy("mAggregatedNotifications") private final ArrayMap<FullyQualifiedGroupKey, CachedSummary> mCanceledSummaries = new ArrayMap<>(); @@ -278,7 +281,11 @@ public class GroupHelper { public void onNotificationRemoved(NotificationRecord record) { try { if (notificationForceGrouping()) { - onNotificationRemoved(record, new ArrayList<>()); + Slog.wtf(TAG, + "This overload of onNotificationRemoved() should not be called if " + + "notification_force_grouping is enabled!", + new Exception("call stack")); + onNotificationRemoved(record, new ArrayList<>(), false); } else { final StatusBarNotification sbn = record.getSbn(); maybeUngroup(sbn, true, sbn.getUserId()); @@ -926,10 +933,12 @@ public class GroupHelper { * * @param record the removed notification * @param notificationList the full notification list from NotificationManagerService + * @param sendingDelete whether the removed notification is being removed in a way that sends + * its {@code deleteIntent} */ @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_FORCE_GROUPING) protected void onNotificationRemoved(final NotificationRecord record, - final List<NotificationRecord> notificationList) { + final List<NotificationRecord> notificationList, boolean sendingDelete) { final StatusBarNotification sbn = record.getSbn(); final String pkgName = sbn.getPackageName(); final int userId = record.getUserId(); @@ -973,9 +982,11 @@ public class GroupHelper { } // Try to cleanup cached summaries if notification was canceled (not snoozed) + // If the notification was cancelled by an action that fires its delete intent, + // also fire it for the cached summary. if (record.isCanceled) { maybeClearCanceledSummariesCache(pkgName, userId, - record.getNotification().getGroup(), notificationList); + record.getNotification().getGroup(), notificationList, sendingDelete); } } } @@ -1759,13 +1770,18 @@ public class GroupHelper { private void cacheCanceledSummary(NotificationRecord record) { final FullyQualifiedGroupKey groupKey = new FullyQualifiedGroupKey(record.getUserId(), record.getSbn().getPackageName(), record.getNotification().getGroup()); - mCanceledSummaries.put(groupKey, new CachedSummary(record.getSbn().getId(), - record.getSbn().getTag(), record.getNotification().getGroup(), record.getKey())); + mCanceledSummaries.put(groupKey, new CachedSummary( + record.getSbn().getId(), + record.getSbn().getTag(), + record.getNotification().getGroup(), + record.getKey(), + record.getNotification().deleteIntent)); } @GuardedBy("mAggregatedNotifications") private void maybeClearCanceledSummariesCache(String pkgName, int userId, - String groupName, List<NotificationRecord> notificationList) { + String groupName, List<NotificationRecord> notificationList, + boolean sendSummaryDelete) { final FullyQualifiedGroupKey findKey = new FullyQualifiedGroupKey(userId, pkgName, groupName); CachedSummary summary = mCanceledSummaries.get(findKey); @@ -1786,6 +1802,9 @@ public class GroupHelper { } if (!stillHasChildren) { removeCachedSummary(pkgName, userId, summary); + if (sendSummaryDelete && summary.deleteIntent != null) { + mCallback.sendAppProvidedSummaryDeleteIntent(pkgName, summary.deleteIntent); + } } } } @@ -1965,7 +1984,8 @@ public class GroupHelper { } } - record CachedSummary(int id, String tag, String originalGroupKey, String key) {} + record CachedSummary(int id, String tag, String originalGroupKey, String key, + @Nullable PendingIntent deleteIntent) { } protected static class NotificationAttributes { public final int flags; @@ -2035,6 +2055,15 @@ public class GroupHelper { // New callbacks for API abuse grouping void removeAppProvidedSummary(String key); + /** + * Send a cached summary's deleteIntent, when the last of its original children is removed. + * + * <p>While technically the group summary was "canceled" much earlier (because it was the + * summary of a sparse group and its children got reparented), the posting package expected + * the summary's deleteIntent to fire when the summary is auto-dismissed. + */ + void sendAppProvidedSummaryDeleteIntent(String pkg, PendingIntent deleteIntent); + void removeNotificationFromCanceledGroup(int userId, String pkg, String groupKey, int cancelReason); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 6fddfb5f90a7..60371d751c4a 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -3211,6 +3211,11 @@ public class NotificationManagerService extends SystemService { } @Override + public void sendAppProvidedSummaryDeleteIntent(String pkg, PendingIntent deleteIntent) { + sendDeleteIntent(deleteIntent, pkg); + } + + @Override public void removeNotificationFromCanceledGroup(int userId, String pkg, String groupKey, int cancelReason) { synchronized (mNotificationLock) { @@ -9898,7 +9903,8 @@ public class NotificationManagerService extends SystemService { if (notificationForceGrouping()) { mHandler.post(() -> { synchronized (mNotificationLock) { - mGroupHelper.onNotificationRemoved(r, mNotificationList); + mGroupHelper.onNotificationRemoved(r, mNotificationList, + /* sendingDelete= */ false); } }); } else { @@ -10826,20 +10832,7 @@ public class NotificationManagerService extends SystemService { // tell the app if (sendDelete) { - final PendingIntent deleteIntent = r.getNotification().deleteIntent; - if (deleteIntent != null) { - try { - // make sure deleteIntent cannot be used to start activities from background - LocalServices.getService(ActivityManagerInternal.class) - .clearPendingIntentAllowBgActivityStarts(deleteIntent.getTarget(), - ALLOWLIST_TOKEN); - deleteIntent.send(); - } catch (PendingIntent.CanceledException ex) { - // do nothing - there's no relevant way to recover, and - // no reason to let this propagate - Slog.w(TAG, "canceled PendingIntent for " + r.getSbn().getPackageName(), ex); - } - } + sendDeleteIntent(r.getNotification().deleteIntent, r.getSbn().getPackageName()); } // Only cancel these if this notification actually got to be posted. @@ -10854,7 +10847,7 @@ public class NotificationManagerService extends SystemService { mHandler.removeCallbacksAndEqualMessages(r.getKey()); mHandler.post(() -> { synchronized (NotificationManagerService.this.mNotificationLock) { - mGroupHelper.onNotificationRemoved(r, mNotificationList); + mGroupHelper.onNotificationRemoved(r, mNotificationList, sendDelete); } }); @@ -10952,6 +10945,21 @@ public class NotificationManagerService extends SystemService { } } + private static void sendDeleteIntent(@Nullable PendingIntent deleteIntent, String fromPkg) { + if (deleteIntent != null) { + try { + // make sure deleteIntent cannot be used to start activities from background + LocalServices.getService(ActivityManagerInternal.class) + .clearPendingIntentAllowBgActivityStarts(deleteIntent.getTarget(), + ALLOWLIST_TOKEN); + deleteIntent.send(); + } catch (PendingIntent.CanceledException ex) { + // There's no relevant way to recover, and no reason to let this propagate + Slog.w(TAG, "canceled PendingIntent for " + fromPkg, ex); + } + } + } + @VisibleForTesting void updateUriPermissions(@Nullable NotificationRecord newRecord, @Nullable NotificationRecord oldRecord, String targetPkg, int targetUserId) { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java index 4a977be2aad9..4eac1d126202 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java @@ -66,6 +66,8 @@ import static org.mockito.Mockito.when; import android.annotation.SuppressLint; import android.app.Notification; import android.app.NotificationChannel; +import android.app.PendingIntent; +import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.Color; import android.graphics.drawable.AdaptiveIconDrawable; @@ -620,6 +622,7 @@ public class GroupHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING) public void testAutoGrouped_singleOngoing_removeOngoingChild() { final String pkg = "package"; @@ -639,7 +642,7 @@ public class GroupHelperTest extends UiServiceTestCase { } // remove ongoing - mGroupHelper.onNotificationRemoved(notifications.get(0)); + mGroupHelper.onNotificationRemoved(notifications.get(0), new ArrayList<>(), false); // Summary is no longer ongoing verify(mCallback).updateAutogroupSummary(anyInt(), anyString(), anyString(), @@ -776,7 +779,7 @@ public class GroupHelperTest extends UiServiceTestCase { } // remove ongoing - mGroupHelper.onNotificationRemoved(notifications.get(1)); + mGroupHelper.onNotificationRemoved(notifications.get(1), new ArrayList<>(), false); // Summary is still ongoing verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), anyString(), anyString(), @@ -936,7 +939,7 @@ public class GroupHelperTest extends UiServiceTestCase { mGroupHelper.onNotificationPosted(r, false); } - mGroupHelper.onNotificationRemoved(notifications.get(0)); + mGroupHelper.onNotificationRemoved(notifications.get(0), new ArrayList<>(), false); // Summary should still be autocancelable verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), anyString(), anyString(), @@ -944,6 +947,7 @@ public class GroupHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING) @DisableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST) public void testDropToZeroRemoveGroup_disableFlag() { final String pkg = "package"; @@ -963,19 +967,20 @@ public class GroupHelperTest extends UiServiceTestCase { Mockito.reset(mCallback); for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) { - mGroupHelper.onNotificationRemoved(posted.remove(0)); + mGroupHelper.onNotificationRemoved(posted.remove(0), new ArrayList<>(), false); } verify(mCallback, never()).removeAutoGroup(anyString()); verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString()); Mockito.reset(mCallback); - mGroupHelper.onNotificationRemoved(posted.remove(0)); + mGroupHelper.onNotificationRemoved(posted.remove(0), new ArrayList<>(), false); verify(mCallback, never()).removeAutoGroup(anyString()); verify(mCallback, times(1)).removeAutoGroupSummary(anyInt(), anyString(), anyString()); } @Test - @EnableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST) + @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, + android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST}) public void testDropToZeroRemoveGroup() { final String pkg = "package"; ArrayList<NotificationRecord> posted = new ArrayList<>(); @@ -994,13 +999,13 @@ public class GroupHelperTest extends UiServiceTestCase { Mockito.reset(mCallback); for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) { - mGroupHelper.onNotificationRemoved(posted.remove(0)); + mGroupHelper.onNotificationRemoved(posted.remove(0), new ArrayList<>(), false); } verify(mCallback, never()).removeAutoGroup(anyString()); verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString()); Mockito.reset(mCallback); - mGroupHelper.onNotificationRemoved(posted.remove(0)); + mGroupHelper.onNotificationRemoved(posted.remove(0), new ArrayList<>(), false); verify(mCallback, never()).removeAutoGroup(anyString()); verify(mCallback, times(1)).removeAutoGroupSummary(anyInt(), anyString(), anyString()); } @@ -1072,6 +1077,7 @@ public class GroupHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING) @DisableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST) public void testNewNotificationsAddedToAutogroup_ifOriginalNotificationsCanceled_alwaysGroup() { final String pkg = "package"; @@ -1091,7 +1097,7 @@ public class GroupHelperTest extends UiServiceTestCase { Mockito.reset(mCallback); for (int i = posted.size() - 2; i >= 0; i--) { - mGroupHelper.onNotificationRemoved(posted.remove(i)); + mGroupHelper.onNotificationRemoved(posted.remove(i), new ArrayList<>(), false); } verify(mCallback, never()).removeAutoGroup(anyString()); verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString()); @@ -1114,7 +1120,8 @@ public class GroupHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST) + @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, + android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST}) public void testNewNotificationsAddedToAutogroup_ifOriginalNotificationsCanceled() { final String pkg = "package"; ArrayList<NotificationRecord> posted = new ArrayList<>(); @@ -1134,7 +1141,7 @@ public class GroupHelperTest extends UiServiceTestCase { Mockito.reset(mCallback); for (int i = posted.size() - 2; i >= 0; i--) { - mGroupHelper.onNotificationRemoved(posted.remove(i)); + mGroupHelper.onNotificationRemoved(posted.remove(i), new ArrayList<>(), false); } verify(mCallback, never()).removeAutoGroup(anyString()); verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString()); @@ -1481,7 +1488,7 @@ public class GroupHelperTest extends UiServiceTestCase { } // Remove last notification (the only one with different icon and color) - mGroupHelper.onNotificationRemoved(notifications.get(lastIdx)); + mGroupHelper.onNotificationRemoved(notifications.get(lastIdx), new ArrayList<>(), false); // Summary should be updated to the common icon and color verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), anyString(), anyString(), @@ -2162,7 +2169,7 @@ public class GroupHelperTest extends UiServiceTestCase { NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i), UserHandle.SYSTEM, "testGrp " + i, false); r.setOverrideGroupKey(expectedGroupKey); - mGroupHelper.onNotificationRemoved(r, notificationList); + mGroupHelper.onNotificationRemoved(r, notificationList, false); } // Check that the autogroup summary is removed verify(mCallback, times(1)).removeAutoGroupSummary(anyInt(), eq(pkg), @@ -2206,7 +2213,7 @@ public class GroupHelperTest extends UiServiceTestCase { Mockito.reset(mCallback); for (NotificationRecord r: childrenToRemove) { notificationList.remove(r); - mGroupHelper.onNotificationRemoved(r, notificationList); + mGroupHelper.onNotificationRemoved(r, notificationList, false); } // Only call onGroupedNotificationRemovedWithDelay with the summary notification mGroupHelper.onGroupedNotificationRemovedWithDelay(summary, notificationList, @@ -2270,7 +2277,7 @@ public class GroupHelperTest extends UiServiceTestCase { Mockito.reset(mCallback); for (NotificationRecord r: childrenToRemove) { notificationList.remove(r); - mGroupHelper.onNotificationRemoved(r, notificationList); + mGroupHelper.onNotificationRemoved(r, notificationList, false); } // Only call onGroupedNotificationRemovedWithDelay with the summary notification mGroupHelper.onGroupedNotificationRemovedWithDelay(summary, notificationList, @@ -2327,7 +2334,7 @@ public class GroupHelperTest extends UiServiceTestCase { for (NotificationRecord r: notificationList) { if (r.getGroupKey().contains(groupToRemove)) { r.isCanceled = true; - mGroupHelper.onNotificationRemoved(r, notificationList); + mGroupHelper.onNotificationRemoved(r, notificationList, false); } } // Only call onGroupedNotificationRemovedWithDelay with the summary notification @@ -2452,7 +2459,7 @@ public class GroupHelperTest extends UiServiceTestCase { true); assertThat(GroupHelper.getSection(notifToInvalidate)).isNull(); notificationList.remove(notifToInvalidate); - mGroupHelper.onNotificationRemoved(notifToInvalidate, notificationList); + mGroupHelper.onNotificationRemoved(notifToInvalidate, notificationList, false); // Check that the autogroup was updated verify(mCallback, never()).removeAutoGroup(anyString()); @@ -4023,7 +4030,7 @@ public class GroupHelperTest extends UiServiceTestCase { //Cancel child 0 => remove cached summary childToRemove.isCanceled = true; notificationListAfterGrouping.remove(childToRemove); - mGroupHelper.onNotificationRemoved(childToRemove, notificationListAfterGrouping); + mGroupHelper.onNotificationRemoved(childToRemove, notificationListAfterGrouping, false); CachedSummary cachedSummary = mGroupHelper.findCanceledSummary(pkg, String.valueOf(id), id, UserHandle.SYSTEM.getIdentifier()); assertThat(cachedSummary).isNull(); @@ -4399,4 +4406,41 @@ public class GroupHelperTest extends UiServiceTestCase { "PeopleSection(priority)"); } + @Test + @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING) + public void onNotificationRemoved_lastChildOfCachedSummary_firesCachedSummaryDeleteIntent() { + final List<NotificationRecord> notificationList = new ArrayList<>(); + final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>(); + final String pkg = "package"; + NotificationRecord onlyChildOfFirstGroup = null; + PendingIntent deleteIntentofFirstSummary = PendingIntent.getActivity(mContext, 1, + new Intent(), PendingIntent.FLAG_IMMUTABLE); + // Post singleton groups, above forced group limit, so they are force grouped + for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) { + NotificationRecord summary = getNotificationRecord(pkg, i, + String.valueOf(i), UserHandle.SYSTEM, "testGrp " + i, true); + notificationList.add(summary); + NotificationRecord child = getNotificationRecord(pkg, i + 42, + String.valueOf(i + 42), UserHandle.SYSTEM, "testGrp " + i, false); + notificationList.add(child); + summaryByGroup.put(summary.getGroupKey(), summary); + if (i == 0) { + onlyChildOfFirstGroup = child; + summary.getNotification().deleteIntent = deleteIntentofFirstSummary; + } + mGroupHelper.onNotificationPostedWithDelay(child, notificationList, summaryByGroup); + summary.isCanceled = true; // simulate removing the app summary + mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup); + } + // Sparse group autogrouping would've removed the summary. + notificationList.remove(0); + + // Now remove the only child of the first (force-grouped, cuz sparse) group. + notificationList.remove(0); + onlyChildOfFirstGroup.isCanceled = true; + mGroupHelper.onNotificationRemoved(onlyChildOfFirstGroup, notificationList, true); + + verify(mCallback).sendAppProvidedSummaryDeleteIntent(eq(pkg), + eq(deleteIntentofFirstSummary)); + } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 37ab541f12da..3727bbefb1d3 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -349,6 +349,7 @@ import com.android.server.wm.WindowManagerInternal; import com.google.android.collect.Lists; import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; @@ -2788,8 +2789,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { nr1.getSbn().getId(), nr1.getSbn().getUserId()); waitForIdle(); - verify(mGroupHelper, times(1)).onNotificationRemoved(eq(nr0), any()); - verify(mGroupHelper, times(1)).onNotificationRemoved(eq(nr1), any()); + verify(mGroupHelper, times(1)).onNotificationRemoved(eq(nr0), any(), eq(false)); + verify(mGroupHelper, times(1)).onNotificationRemoved(eq(nr1), any(), eq(false)); // GroupHelper would send 'remove summary' event mService.clearAutogroupSummaryLocked(nr1.getUserId(), nr1.getSbn().getPackageName(), @@ -3155,8 +3156,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { waitForIdle(); // Check that onGroupedNotificationRemovedWithDelay was called only once - verify(mGroupHelper, times(1)).onNotificationRemoved(eq(r1), any()); - verify(mGroupHelper, times(1)).onNotificationRemoved(eq(r2), any()); + verify(mGroupHelper, times(1)).onNotificationRemoved(eq(r1), any(), eq(false)); + verify(mGroupHelper, times(1)).onNotificationRemoved(eq(r2), any(), eq(false)); verify(mGroupHelper, times(1)).onGroupedNotificationRemovedWithDelay(eq(summary), any(), any()); } @@ -3201,9 +3202,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { waitForIdle(); // Check that onGroupedNotificationRemovedWithDelay was never called: summary was canceled - verify(mGroupHelper, times(1)).onNotificationRemoved(eq(r1), any()); - verify(mGroupHelper, times(1)).onNotificationRemoved(eq(r2), any()); - verify(mGroupHelper, times(1)).onNotificationRemoved(eq(summary), any()); + verify(mGroupHelper, times(1)).onNotificationRemoved(eq(r1), any(), eq(false)); + verify(mGroupHelper, times(1)).onNotificationRemoved(eq(r2), any(), eq(false)); + verify(mGroupHelper, times(1)).onNotificationRemoved(eq(summary), any(), eq(false)); verify(mGroupHelper, never()).onGroupedNotificationRemovedWithDelay(any(), any(), any()); } @@ -14122,9 +14123,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { waitForIdle(); // Check that child notifications are also removed - verify(mGroupHelper, times(1)).onNotificationRemoved(eq(aggregateSummary), any()); - verify(mGroupHelper, times(1)).onNotificationRemoved(eq(nr0), any()); - verify(mGroupHelper, times(1)).onNotificationRemoved(eq(nr1), any()); + verify(mGroupHelper, times(1)).onNotificationRemoved(eq(aggregateSummary), any(), + eq(false)); + verify(mGroupHelper, times(1)).onNotificationRemoved(eq(nr0), any(), eq(false)); + verify(mGroupHelper, times(1)).onNotificationRemoved(eq(nr1), any(), eq(false)); // Make sure the summary was removed and not re-posted assertThat(mService.getNotificationRecordCount()).isEqualTo(0); @@ -18659,4 +18661,35 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } } + @Test + @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING) + public void clearAll_fromUser_willSendDeleteIntentForCachedSummaries() throws Exception { + NotificationRecord n = generateNotificationRecord( + mTestNotificationChannel, 1, "group", true); + mBinderService.enqueueNotificationWithTag(mPkg, mPkg, "tag", + n.getSbn().getId(), n.getSbn().getNotification(), n.getSbn().getUserId()); + waitForIdle(); + n = Iterables.getOnlyElement(mService.mNotificationList); + + mService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(), n.getUserId()); + waitForIdle(); + + verify(mGroupHelper).onNotificationRemoved(eq(n), any(), eq(true)); + } + + @Test + @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING) + public void cancel_fromApp_willNotSendDeleteIntentForCachedSummaries() throws Exception { + NotificationRecord n = generateNotificationRecord( + mTestNotificationChannel, 1, "group", true); + mBinderService.enqueueNotificationWithTag(mPkg, mPkg, "tag", + n.getSbn().getId(), n.getSbn().getNotification(), n.getSbn().getUserId()); + waitForIdle(); + n = Iterables.getOnlyElement(mService.mNotificationList); + + mBinderService.cancelAllNotifications(mPkg, mUserId); + waitForIdle(); + + verify(mGroupHelper).onNotificationRemoved(eq(n), any(), eq(false)); + } } |