Loading services/core/java/com/android/server/notification/GroupHelper.java +16 −0 Original line number Original line Diff line number Diff line Loading @@ -1905,6 +1905,22 @@ public class GroupHelper { } } } } /** * Checks if the notification was a summary that was canceled because its children * were auto-grouped. * * @param record the notification to check * @return true if the notification was a canceled summary */ boolean wasSummaryBeforeAutoGrouping(@NonNull final NotificationRecord record) { synchronized (mAggregatedNotifications) { return (findCanceledSummary(record.getSbn().getPackageName(), record.getSbn().getTag(), record.getSbn().getId(), record.getSbn().getUserId(), record.getSbn().getNotification().getGroup()) != null); } } @GuardedBy("mAggregatedNotifications") @GuardedBy("mAggregatedNotifications") private void removeCachedSummary(String pkgName, int userId, CachedSummary summary) { private void removeCachedSummary(String pkgName, int userId, CachedSummary summary) { final FullyQualifiedGroupKey key = new FullyQualifiedGroupKey(userId, pkgName, final FullyQualifiedGroupKey key = new FullyQualifiedGroupKey(userId, pkgName, Loading services/core/java/com/android/server/notification/NotificationManagerService.java +21 −6 Original line number Original line Diff line number Diff line Loading @@ -151,6 +151,7 @@ import static android.service.notification.NotificationListenerService.REASON_CH import static android.service.notification.NotificationListenerService.REASON_CLEAR_DATA; import static android.service.notification.NotificationListenerService.REASON_CLEAR_DATA; import static android.service.notification.NotificationListenerService.REASON_CLICK; import static android.service.notification.NotificationListenerService.REASON_CLICK; import static android.service.notification.NotificationListenerService.REASON_ERROR; import static android.service.notification.NotificationListenerService.REASON_ERROR; import static android.service.notification.NotificationListenerService.REASON_GROUP_OPTIMIZATION; import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED; import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED; import static android.service.notification.NotificationListenerService.REASON_LISTENER_CANCEL; import static android.service.notification.NotificationListenerService.REASON_LISTENER_CANCEL; import static android.service.notification.NotificationListenerService.REASON_LISTENER_CANCEL_ALL; import static android.service.notification.NotificationListenerService.REASON_LISTENER_CANCEL_ALL; Loading Loading @@ -7707,7 +7708,7 @@ public class NotificationManagerService extends SystemService { Binder.getCallingPid(), r.getSbn().getPackageName(), Binder.getCallingPid(), r.getSbn().getPackageName(), r.getSbn().getTag(), r.getSbn().getId(), 0, 0, r.getSbn().getTag(), r.getSbn().getId(), 0, 0, false, r.getUserId(), false, r.getUserId(), NotificationListenerService.REASON_GROUP_OPTIMIZATION, null); REASON_GROUP_OPTIMIZATION, null); } } } } Loading Loading @@ -7748,8 +7749,8 @@ public class NotificationManagerService extends SystemService { cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), groupSummary.getSbn().getPackageName(), groupSummary.getSbn().getPackageName(), groupSummary.getSbn().getTag(), groupSummary.getSbn().getTag(), groupSummary.getSbn().getId(), 0, 0, false, groupSummary.getUserId(), groupSummary.getSbn().getId(), 0, 0, false, NotificationListenerService.REASON_GROUP_OPTIMIZATION, null); groupSummary.getUserId(), REASON_GROUP_OPTIMIZATION, null); } } } } } } Loading Loading @@ -11121,7 +11122,7 @@ public class NotificationManagerService extends SystemService { // Save it for users of getHistoricalNotifications(), unless the whole channel was deleted // Save it for users of getHistoricalNotifications(), unless the whole channel was deleted if (reason != REASON_CHANNEL_REMOVED) { if (reason != REASON_CHANNEL_REMOVED) { mArchive.record(r.getSbn(), reason); mArchive.record(getSbnForArchive(r, reason), reason); } } final long now = System.currentTimeMillis(); final long now = System.currentTimeMillis(); Loading @@ -11142,6 +11143,20 @@ public class NotificationManagerService extends SystemService { } } } } @GuardedBy("mNotificationLock") private StatusBarNotification getSbnForArchive(@NonNull NotificationRecord r, int reason) { final StatusBarNotification sbn = r.getSbn(); if (notificationClassification()) { if (reason == REASON_GROUP_OPTIMIZATION && mGroupHelper.wasSummaryBeforeAutoGrouping(r)) { StatusBarNotification sbnClone = sbn.cloneLight(); sbnClone.getNotification().flags |= FLAG_GROUP_SUMMARY; return sbnClone; } } return sbn; } private static void sendDeleteIntent(@Nullable PendingIntent deleteIntent, String fromPkg) { private static void sendDeleteIntent(@Nullable PendingIntent deleteIntent, String fromPkg) { if (deleteIntent != null) { if (deleteIntent != null) { try { try { Loading services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +81 −0 Original line number Original line Diff line number Diff line Loading @@ -12700,6 +12700,87 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertEquals(0, notifs.length); assertEquals(0, notifs.length); } } @Test @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION) public void testArchiveCanceledBundledGroupSummary_restoresSummaryFlag() throws Exception { // Enables Notification History setting setUpPrefsForHistory(mUserId, true /* =enabled */); // Add a group summary + child notification final String originalGroupName = "originalGroup"; final int summaryId = 0; final NotificationRecord r1 = generateNotificationRecord(mTestNotificationChannel, summaryId + 1, originalGroupName, false); mService.addNotification(r1); final NotificationRecord summary = generateNotificationRecord(mTestNotificationChannel, summaryId, originalGroupName, true); mService.addNotification(summary); final String originalGroupKey = summary.getGroupKey(); final int originalSummaryFlags = summary.getFlags(); // Regroup first child notification r1.setOverrideGroupKey("newGroup"); when(mGroupHelper.wasSummaryBeforeAutoGrouping(summary)).thenReturn(true); // Check that removeAppProvidedSummaryOnClassificationLocked is null // => there is still one child left in the original group assertThat(mService.removeAppProvidedSummaryOnClassificationLocked(r1.getKey(), originalGroupKey)).isEqualTo(summary); waitForIdle(); verify(mWorkerHandler, times(1)).scheduleCancelNotification(any(), eq(summaryId)); assertThat(mService.mSummaryByGroupKey).doesNotContainKey(originalGroupKey); assertThat(summary.getNotification().flags & FLAG_GROUP_SUMMARY).isEqualTo(0); // Checks that notification history's recently canceled archive contains the notification. StatusBarNotification[] notifs = mBinderService.getHistoricalNotificationsWithAttribution( mPkg, mContext.getAttributionTag(), 5 /* count */, false /* includeSnoozed */); waitForIdle(); assertThat(notifs).hasLength(1); assertThat(notifs[0].getKey()).isEqualTo(summary.getKey()); assertThat(notifs[0].getNotification().flags).isEqualTo(originalSummaryFlags); assertThat(notifs[0].getNotification().flags & FLAG_GROUP_SUMMARY).isEqualTo( FLAG_GROUP_SUMMARY); } @Test @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION) public void testArchiveCanceledBundledGroupSummaryOtherReason_doesNotRestoreSummaryFlag() throws Exception { // Enables Notification History setting setUpPrefsForHistory(mUserId, true /* =enabled */); // Add a group summary + child notification final String originalGroupName = "originalGroup"; final int summaryId = 0; final NotificationRecord r1 = generateNotificationRecord(mTestNotificationChannel, summaryId + 1, originalGroupName, false); mService.addNotification(r1); final NotificationRecord summary = generateNotificationRecord(mTestNotificationChannel, summaryId, originalGroupName, true); mService.addNotification(summary); // Regroup first child notification r1.setOverrideGroupKey("newGroup"); when(mGroupHelper.wasSummaryBeforeAutoGrouping(summary)).thenReturn(true); // Remove FLAG_GROUP_SUMMARY summary.getSbn().getNotification().flags = (summary.mOriginalFlags & ~FLAG_GROUP_SUMMARY); // Cancel the summary with REASON_APP_CANCEL mBinderService.cancelNotificationWithTag(mPkg, mPkg, summary.getSbn().getTag(), summary.getSbn().getId(), summary.getSbn().getUserId()); waitForIdle(); verify(mWorkerHandler, times(1)).scheduleCancelNotification(any(), eq(summaryId)); // Checks that notification history's recently canceled archive contains the notification. StatusBarNotification[] notifs = mBinderService.getHistoricalNotificationsWithAttribution( mPkg, mContext.getAttributionTag(), 5 /* count */, false /* includeSnoozed */); waitForIdle(); assertThat(notifs).hasLength(1); assertThat(notifs[0].getKey()).isEqualTo(summary.getKey()); assertThat(notifs[0].getNotification().flags & FLAG_GROUP_SUMMARY).isEqualTo(0); } @Test @Test public void testNotificationHistory_addNoisyNotification() throws Exception { public void testNotificationHistory_addNoisyNotification() throws Exception { NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel, NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel, Loading
services/core/java/com/android/server/notification/GroupHelper.java +16 −0 Original line number Original line Diff line number Diff line Loading @@ -1905,6 +1905,22 @@ public class GroupHelper { } } } } /** * Checks if the notification was a summary that was canceled because its children * were auto-grouped. * * @param record the notification to check * @return true if the notification was a canceled summary */ boolean wasSummaryBeforeAutoGrouping(@NonNull final NotificationRecord record) { synchronized (mAggregatedNotifications) { return (findCanceledSummary(record.getSbn().getPackageName(), record.getSbn().getTag(), record.getSbn().getId(), record.getSbn().getUserId(), record.getSbn().getNotification().getGroup()) != null); } } @GuardedBy("mAggregatedNotifications") @GuardedBy("mAggregatedNotifications") private void removeCachedSummary(String pkgName, int userId, CachedSummary summary) { private void removeCachedSummary(String pkgName, int userId, CachedSummary summary) { final FullyQualifiedGroupKey key = new FullyQualifiedGroupKey(userId, pkgName, final FullyQualifiedGroupKey key = new FullyQualifiedGroupKey(userId, pkgName, Loading
services/core/java/com/android/server/notification/NotificationManagerService.java +21 −6 Original line number Original line Diff line number Diff line Loading @@ -151,6 +151,7 @@ import static android.service.notification.NotificationListenerService.REASON_CH import static android.service.notification.NotificationListenerService.REASON_CLEAR_DATA; import static android.service.notification.NotificationListenerService.REASON_CLEAR_DATA; import static android.service.notification.NotificationListenerService.REASON_CLICK; import static android.service.notification.NotificationListenerService.REASON_CLICK; import static android.service.notification.NotificationListenerService.REASON_ERROR; import static android.service.notification.NotificationListenerService.REASON_ERROR; import static android.service.notification.NotificationListenerService.REASON_GROUP_OPTIMIZATION; import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED; import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED; import static android.service.notification.NotificationListenerService.REASON_LISTENER_CANCEL; import static android.service.notification.NotificationListenerService.REASON_LISTENER_CANCEL; import static android.service.notification.NotificationListenerService.REASON_LISTENER_CANCEL_ALL; import static android.service.notification.NotificationListenerService.REASON_LISTENER_CANCEL_ALL; Loading Loading @@ -7707,7 +7708,7 @@ public class NotificationManagerService extends SystemService { Binder.getCallingPid(), r.getSbn().getPackageName(), Binder.getCallingPid(), r.getSbn().getPackageName(), r.getSbn().getTag(), r.getSbn().getId(), 0, 0, r.getSbn().getTag(), r.getSbn().getId(), 0, 0, false, r.getUserId(), false, r.getUserId(), NotificationListenerService.REASON_GROUP_OPTIMIZATION, null); REASON_GROUP_OPTIMIZATION, null); } } } } Loading Loading @@ -7748,8 +7749,8 @@ public class NotificationManagerService extends SystemService { cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), groupSummary.getSbn().getPackageName(), groupSummary.getSbn().getPackageName(), groupSummary.getSbn().getTag(), groupSummary.getSbn().getTag(), groupSummary.getSbn().getId(), 0, 0, false, groupSummary.getUserId(), groupSummary.getSbn().getId(), 0, 0, false, NotificationListenerService.REASON_GROUP_OPTIMIZATION, null); groupSummary.getUserId(), REASON_GROUP_OPTIMIZATION, null); } } } } } } Loading Loading @@ -11121,7 +11122,7 @@ public class NotificationManagerService extends SystemService { // Save it for users of getHistoricalNotifications(), unless the whole channel was deleted // Save it for users of getHistoricalNotifications(), unless the whole channel was deleted if (reason != REASON_CHANNEL_REMOVED) { if (reason != REASON_CHANNEL_REMOVED) { mArchive.record(r.getSbn(), reason); mArchive.record(getSbnForArchive(r, reason), reason); } } final long now = System.currentTimeMillis(); final long now = System.currentTimeMillis(); Loading @@ -11142,6 +11143,20 @@ public class NotificationManagerService extends SystemService { } } } } @GuardedBy("mNotificationLock") private StatusBarNotification getSbnForArchive(@NonNull NotificationRecord r, int reason) { final StatusBarNotification sbn = r.getSbn(); if (notificationClassification()) { if (reason == REASON_GROUP_OPTIMIZATION && mGroupHelper.wasSummaryBeforeAutoGrouping(r)) { StatusBarNotification sbnClone = sbn.cloneLight(); sbnClone.getNotification().flags |= FLAG_GROUP_SUMMARY; return sbnClone; } } return sbn; } private static void sendDeleteIntent(@Nullable PendingIntent deleteIntent, String fromPkg) { private static void sendDeleteIntent(@Nullable PendingIntent deleteIntent, String fromPkg) { if (deleteIntent != null) { if (deleteIntent != null) { try { try { Loading
services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +81 −0 Original line number Original line Diff line number Diff line Loading @@ -12700,6 +12700,87 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertEquals(0, notifs.length); assertEquals(0, notifs.length); } } @Test @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION) public void testArchiveCanceledBundledGroupSummary_restoresSummaryFlag() throws Exception { // Enables Notification History setting setUpPrefsForHistory(mUserId, true /* =enabled */); // Add a group summary + child notification final String originalGroupName = "originalGroup"; final int summaryId = 0; final NotificationRecord r1 = generateNotificationRecord(mTestNotificationChannel, summaryId + 1, originalGroupName, false); mService.addNotification(r1); final NotificationRecord summary = generateNotificationRecord(mTestNotificationChannel, summaryId, originalGroupName, true); mService.addNotification(summary); final String originalGroupKey = summary.getGroupKey(); final int originalSummaryFlags = summary.getFlags(); // Regroup first child notification r1.setOverrideGroupKey("newGroup"); when(mGroupHelper.wasSummaryBeforeAutoGrouping(summary)).thenReturn(true); // Check that removeAppProvidedSummaryOnClassificationLocked is null // => there is still one child left in the original group assertThat(mService.removeAppProvidedSummaryOnClassificationLocked(r1.getKey(), originalGroupKey)).isEqualTo(summary); waitForIdle(); verify(mWorkerHandler, times(1)).scheduleCancelNotification(any(), eq(summaryId)); assertThat(mService.mSummaryByGroupKey).doesNotContainKey(originalGroupKey); assertThat(summary.getNotification().flags & FLAG_GROUP_SUMMARY).isEqualTo(0); // Checks that notification history's recently canceled archive contains the notification. StatusBarNotification[] notifs = mBinderService.getHistoricalNotificationsWithAttribution( mPkg, mContext.getAttributionTag(), 5 /* count */, false /* includeSnoozed */); waitForIdle(); assertThat(notifs).hasLength(1); assertThat(notifs[0].getKey()).isEqualTo(summary.getKey()); assertThat(notifs[0].getNotification().flags).isEqualTo(originalSummaryFlags); assertThat(notifs[0].getNotification().flags & FLAG_GROUP_SUMMARY).isEqualTo( FLAG_GROUP_SUMMARY); } @Test @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION) public void testArchiveCanceledBundledGroupSummaryOtherReason_doesNotRestoreSummaryFlag() throws Exception { // Enables Notification History setting setUpPrefsForHistory(mUserId, true /* =enabled */); // Add a group summary + child notification final String originalGroupName = "originalGroup"; final int summaryId = 0; final NotificationRecord r1 = generateNotificationRecord(mTestNotificationChannel, summaryId + 1, originalGroupName, false); mService.addNotification(r1); final NotificationRecord summary = generateNotificationRecord(mTestNotificationChannel, summaryId, originalGroupName, true); mService.addNotification(summary); // Regroup first child notification r1.setOverrideGroupKey("newGroup"); when(mGroupHelper.wasSummaryBeforeAutoGrouping(summary)).thenReturn(true); // Remove FLAG_GROUP_SUMMARY summary.getSbn().getNotification().flags = (summary.mOriginalFlags & ~FLAG_GROUP_SUMMARY); // Cancel the summary with REASON_APP_CANCEL mBinderService.cancelNotificationWithTag(mPkg, mPkg, summary.getSbn().getTag(), summary.getSbn().getId(), summary.getSbn().getUserId()); waitForIdle(); verify(mWorkerHandler, times(1)).scheduleCancelNotification(any(), eq(summaryId)); // Checks that notification history's recently canceled archive contains the notification. StatusBarNotification[] notifs = mBinderService.getHistoricalNotificationsWithAttribution( mPkg, mContext.getAttributionTag(), 5 /* count */, false /* includeSnoozed */); waitForIdle(); assertThat(notifs).hasLength(1); assertThat(notifs[0].getKey()).isEqualTo(summary.getKey()); assertThat(notifs[0].getNotification().flags & FLAG_GROUP_SUMMARY).isEqualTo(0); } @Test @Test public void testNotificationHistory_addNoisyNotification() throws Exception { public void testNotificationHistory_addNoisyNotification() throws Exception { NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel, NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,