diff options
3 files changed, 277 insertions, 48 deletions
diff --git a/services/core/java/com/android/server/notification/GroupHelper.java b/services/core/java/com/android/server/notification/GroupHelper.java index 5914dbe44b0b..3a77d2bb67a9 100644 --- a/services/core/java/com/android/server/notification/GroupHelper.java +++ b/services/core/java/com/android/server/notification/GroupHelper.java @@ -92,10 +92,16 @@ public class GroupHelper { static final int REGROUP_REASON_CHANNEL_UPDATE = 0; // Regrouping needed because of notification bundling static final int REGROUP_REASON_BUNDLE = 1; + // Regrouping needed because of notification unbundling + static final int REGROUP_REASON_UNBUNDLE = 2; + // Regrouping needed because of notification unbundling + the original group summary exists + static final int REGROUP_REASON_UNBUNDLE_ORIGINAL_GROUP = 3; @IntDef(prefix = { "REGROUP_REASON_" }, value = { REGROUP_REASON_CHANNEL_UPDATE, REGROUP_REASON_BUNDLE, + REGROUP_REASON_UNBUNDLE, + REGROUP_REASON_UNBUNDLE_ORIGINAL_GROUP, }) @Retention(RetentionPolicy.SOURCE) @interface RegroupingReason {} @@ -103,7 +109,6 @@ public class GroupHelper { private final Callback mCallback; private final int mAutoGroupAtCount; private final int mAutogroupSparseGroupsAtCount; - private final int mAutoGroupRegroupingAtCount; private final Context mContext; private final PackageManager mPackageManager; private boolean mIsTestHarnessExempted; @@ -190,11 +195,6 @@ public class GroupHelper { mContext = context; mPackageManager = packageManager; mAutogroupSparseGroupsAtCount = autoGroupSparseGroupsAtCount; - if (notificationRegroupOnClassification()) { - mAutoGroupRegroupingAtCount = 1; - } else { - mAutoGroupRegroupingAtCount = mAutoGroupAtCount; - } NOTIFICATION_SHADE_SECTIONS = getNotificationShadeSections(); } @@ -797,7 +797,8 @@ public class GroupHelper { Slog.v(TAG, "isGroupChildInDifferentBundleThanSummary: " + record); } moveNotificationsToNewSection(record.getUserId(), pkgName, - List.of(new NotificationMoveOp(record, null, fullAggregateGroupKey))); + List.of(new NotificationMoveOp(record, null, fullAggregateGroupKey)), + REGROUP_REASON_BUNDLE); return; } } @@ -945,6 +946,27 @@ public class GroupHelper { } } + /** + * Called when a notification that was classified (bundled) is restored to its original channel. + * The notification will be restored to its original group, if any/if summary still exists. + * Otherwise it will be moved to the appropriate section as an ungrouped notification. + * + * @param record the notification which had its channel updated + * @param originalSummaryExists the original group summary exists + */ + @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_FORCE_GROUPING) + public void onNotificationUnbundled(final NotificationRecord record, + final boolean originalSummaryExists) { + synchronized (mAggregatedNotifications) { + ArrayMap<String, NotificationRecord> notificationsToCheck = new ArrayMap<>(); + notificationsToCheck.put(record.getKey(), record); + regroupNotifications(record.getUserId(), record.getSbn().getPackageName(), + notificationsToCheck, + originalSummaryExists ? REGROUP_REASON_UNBUNDLE_ORIGINAL_GROUP + : REGROUP_REASON_UNBUNDLE); + } + } + @GuardedBy("mAggregatedNotifications") private void regroupNotifications(int userId, String pkgName, ArrayMap<String, NotificationRecord> notificationsToCheck, @@ -973,7 +995,7 @@ public class GroupHelper { // Batch move to new section if (!notificationsToMove.isEmpty()) { - moveNotificationsToNewSection(userId, pkgName, notificationsToMove); + moveNotificationsToNewSection(userId, pkgName, notificationsToMove, regroupingReason); } } @@ -1093,7 +1115,7 @@ public class GroupHelper { @GuardedBy("mAggregatedNotifications") private void moveNotificationsToNewSection(final int userId, final String pkgName, - final List<NotificationMoveOp> notificationsToMove) { + final List<NotificationMoveOp> notificationsToMove, int regroupingReason) { record GroupUpdateOp(FullyQualifiedGroupKey groupKey, NotificationRecord record, boolean hasSummary) { } // Bundled operations to apply to groups affected by the channel update @@ -1111,7 +1133,8 @@ public class GroupHelper { if (DEBUG) { Log.i(TAG, "moveNotificationToNewSection: " + record + " " + newFullAggregateGroupKey - + " from: " + oldFullAggregateGroupKey); + + " from: " + oldFullAggregateGroupKey + " regroupingReason: " + + regroupingReason); } // Update/remove aggregate summary for old group @@ -1140,28 +1163,35 @@ public class GroupHelper { // Add moved notifications to the ungrouped list for new group and do grouping // after all notifications have been handled if (newFullAggregateGroupKey != null) { - final ArrayMap<String, NotificationAttributes> newAggregatedNotificationsAttrs = + if (notificationRegroupOnClassification() + && regroupingReason == REGROUP_REASON_UNBUNDLE_ORIGINAL_GROUP) { + // Just reset override group key, original summary exists + // => will be grouped back to its original group + record.setOverrideGroupKey(null); + } else { + final ArrayMap<String, NotificationAttributes> newAggregatedNotificationsAttrs = mAggregatedNotifications.getOrDefault(newFullAggregateGroupKey, new ArrayMap<>()); - boolean hasSummary = !newAggregatedNotificationsAttrs.isEmpty(); - ArrayMap<String, NotificationAttributes> ungrouped = + boolean hasSummary = !newAggregatedNotificationsAttrs.isEmpty(); + ArrayMap<String, NotificationAttributes> ungrouped = mUngroupedAbuseNotifications.getOrDefault(newFullAggregateGroupKey, new ArrayMap<>()); - ungrouped.put(record.getKey(), new NotificationAttributes( + ungrouped.put(record.getKey(), new NotificationAttributes( record.getFlags(), record.getNotification().getSmallIcon(), record.getNotification().color, record.getNotification().visibility, record.getNotification().getGroupAlertBehavior(), record.getChannel().getId())); - mUngroupedAbuseNotifications.put(newFullAggregateGroupKey, ungrouped); + mUngroupedAbuseNotifications.put(newFullAggregateGroupKey, ungrouped); - record.setOverrideGroupKey(null); + record.setOverrideGroupKey(null); - // Only add once, for triggering notification - if (!groupsToUpdate.containsKey(newFullAggregateGroupKey)) { - groupsToUpdate.put(newFullAggregateGroupKey, - new GroupUpdateOp(newFullAggregateGroupKey, record, hasSummary)); + // Only add once, for triggering notification + if (!groupsToUpdate.containsKey(newFullAggregateGroupKey)) { + groupsToUpdate.put(newFullAggregateGroupKey, + new GroupUpdateOp(newFullAggregateGroupKey, record, hasSummary)); + } } } } @@ -1176,7 +1206,7 @@ public class GroupHelper { NotificationRecord triggeringNotification = groupsToUpdate.get(groupKey).record; boolean hasSummary = groupsToUpdate.get(groupKey).hasSummary; //Group needs to be created/updated - if (ungrouped.size() >= mAutoGroupRegroupingAtCount + if (ungrouped.size() >= mAutoGroupAtCount || (hasSummary && !aggregatedNotificationsAttrs.isEmpty())) { NotificationSectioner sectioner = getSection(triggeringNotification); if (sectioner == null) { diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 4d0c7ec64317..539f7b1963da 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -7190,13 +7190,16 @@ public class NotificationManagerService extends SystemService { Slog.i(TAG, "Removing app summary (all children bundled): " + groupSummary); } - canceledSummary = groupSummary; - mSummaryByGroupKey.remove(oldGroupKey); - cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), + if (convertSummaryToNotificationLocked(groupSummary.getKey())) { + groupSummary.isCanceled = true; + canceledSummary = groupSummary; + mSummaryByGroupKey.remove(oldGroupKey); + cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), groupSummary.getSbn().getPackageName(), groupSummary.getSbn().getTag(), groupSummary.getSbn().getId(), 0, 0, false, groupSummary.getUserId(), NotificationListenerService.REASON_GROUP_OPTIMIZATION, null); + } } } } 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 cc0286508cdc..632c3e6d8fb0 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java @@ -2298,6 +2298,7 @@ public class GroupHelperTest extends UiServiceTestCase { final String pkg = "package"; final String expectedGroupKey_alerting = GroupHelper.getFullAggregateGroupKey(pkg, AGGREGATE_GROUP_KEY + "AlertingSection", UserHandle.SYSTEM.getIdentifier()); + final int numNotifications = 2 * AUTOGROUP_AT_COUNT; int numNotificationChannel1 = 0; final NotificationChannel channel1 = new NotificationChannel("TEST_CHANNEL_ID1", "TEST_CHANNEL_ID1", IMPORTANCE_DEFAULT); @@ -2307,7 +2308,7 @@ public class GroupHelperTest extends UiServiceTestCase { final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>(); // Post notifications with different channels that autogroup within the same section NotificationRecord r; - for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) { + for (int i = 0; i < numNotifications; i++) { if (i % 2 == 0) { r = getNotificationRecord(pkg, i, String.valueOf(i), UserHandle.SYSTEM, "testGrp " + i, false, channel1); @@ -2324,12 +2325,12 @@ public class GroupHelperTest extends UiServiceTestCase { "TEST_CHANNEL_ID1"); verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(), eq(expectedGroupKey_alerting), anyInt(), eq(expectedSummaryAttr)); - verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(), + verify(mCallback, times(numNotifications)).addAutoGroup(anyString(), eq(expectedGroupKey_alerting), eq(true)); verify(mCallback, never()).removeAutoGroup(anyString()); verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString()); - verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(), - any()); + verify(mCallback, times(numNotifications - AUTOGROUP_AT_COUNT)).updateAutogroupSummary( + anyInt(), anyString(), anyString(), any()); Mockito.reset(mCallback); // Update channel1's importance @@ -2375,7 +2376,7 @@ public class GroupHelperTest extends UiServiceTestCase { final List<NotificationRecord> notificationList = new ArrayList<>(); final String pkg = "package"; final int summaryId = 0; - final int numChildNotif = 4; + final int numChildNotif = 2 * AUTOGROUP_AT_COUNT; // Create an app-provided group: summary + child notifications final NotificationChannel channel1 = new NotificationChannel("TEST_CHANNEL_ID1", @@ -2435,8 +2436,8 @@ public class GroupHelperTest extends UiServiceTestCase { eq(expectedGroupKey_social), eq(true)); verify(mCallback, never()).removeAutoGroup(anyString()); verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString()); - verify(mCallback, times(numChildNotif / 2)).updateAutogroupSummary(anyInt(), anyString(), - anyString(), any()); + verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(), + any()); verify(mCallback, times(numChildNotif)).removeAppProvidedSummaryOnClassification( anyString(), eq(originalAppGroupKey)); } @@ -2513,9 +2514,10 @@ public class GroupHelperTest extends UiServiceTestCase { final List<NotificationRecord> notificationList = new ArrayList<>(); final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>(); final String pkg = "package"; + final int numChildNotifications = AUTOGROUP_AT_COUNT; // Post singleton groups, above forced group limit - for (int i = 0; i < AUTOGROUP_SINGLETONS_AT_COUNT; i++) { + for (int i = 0; i < numChildNotifications; i++) { NotificationRecord summary = getNotificationRecord(pkg, i, String.valueOf(i), UserHandle.SYSTEM, "testGrp " + i, true); notificationList.add(summary); @@ -2545,13 +2547,13 @@ public class GroupHelperTest extends UiServiceTestCase { // Check that notifications are forced grouped and app-provided summaries are canceled verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(), eq(expectedGroupKey_social), anyInt(), eq(expectedSummaryAttr_social)); - verify(mCallback, times(AUTOGROUP_SINGLETONS_AT_COUNT)).addAutoGroup(anyString(), + verify(mCallback, times(numChildNotifications)).addAutoGroup(anyString(), eq(expectedGroupKey_social), eq(true)); verify(mCallback, never()).removeAutoGroup(anyString()); verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString()); - verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), anyString(), anyString(), + verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(), any()); - verify(mCallback, times(2)).removeAppProvidedSummaryOnClassification( + verify(mCallback, times(numChildNotifications)).removeAppProvidedSummaryOnClassification( anyString(), anyString()); // Adjust group key and cancel summaries @@ -2593,14 +2595,16 @@ public class GroupHelperTest extends UiServiceTestCase { AGGREGATE_GROUP_KEY + "AlertingSection", UserHandle.SYSTEM.getIdentifier()); String expectedTriggeringKey = null; // Post singleton groups, above forced group limit - for (int i = 0; i < AUTOGROUP_SINGLETONS_AT_COUNT; i++) { + 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); - expectedTriggeringKey = child.getKey(); + if (i == AUTOGROUP_SINGLETONS_AT_COUNT - 1) { + expectedTriggeringKey = child.getKey(); + } summaryByGroup.put(summary.getGroupKey(), summary); mGroupHelper.onNotificationPostedWithDelay(child, notificationList, summaryByGroup); summary.isCanceled = true; // simulate removing the app summary @@ -2611,14 +2615,8 @@ public class GroupHelperTest extends UiServiceTestCase { verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), eq(expectedTriggeringKey), eq(expectedGroupKey_alerting), anyInt(), eq(getNotificationAttributes(BASE_FLAGS))); - verify(mCallback, times(AUTOGROUP_SINGLETONS_AT_COUNT)).addAutoGroup(anyString(), - eq(expectedGroupKey_alerting), eq(true)); verify(mCallback, never()).removeAutoGroup(anyString()); verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString()); - verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(), - any()); - verify(mCallback, times(AUTOGROUP_SINGLETONS_AT_COUNT)).removeAppProvidedSummary( - anyString()); assertThat(mGroupHelper.findCanceledSummary(pkg, String.valueOf(0), 0, UserHandle.SYSTEM.getIdentifier())).isNotNull(); assertThat(mGroupHelper.findCanceledSummary(pkg, String.valueOf(1), 1, @@ -2645,12 +2643,12 @@ public class GroupHelperTest extends UiServiceTestCase { // Check that all notifications are moved to the social section group verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(), eq(expectedGroupKey_social), anyInt(), eq(expectedSummaryAttr_social)); - verify(mCallback, times(AUTOGROUP_SINGLETONS_AT_COUNT)).addAutoGroup(anyString(), + verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(), eq(expectedGroupKey_social), eq(true)); // Check that the alerting section group is removed verify(mCallback, times(1)).removeAutoGroupSummary(anyInt(), eq(pkg), eq(expectedGroupKey_alerting)); - verify(mCallback, times(AUTOGROUP_SINGLETONS_AT_COUNT)).updateAutogroupSummary(anyInt(), + verify(mCallback, times(AUTOGROUP_AT_COUNT - 1)).updateAutogroupSummary(anyInt(), anyString(), anyString(), any()); } @@ -2666,7 +2664,7 @@ public class GroupHelperTest extends UiServiceTestCase { final String pkg = "package"; final int summaryId = 0; - final int numChildren = 3; + final int numChildren = AUTOGROUP_AT_COUNT; // Post a regular/valid group: summary + notifications NotificationRecord summary = getNotificationRecord(pkg, summaryId, String.valueOf(summaryId), UserHandle.SYSTEM, "testGrp", true); @@ -2706,13 +2704,211 @@ public class GroupHelperTest extends UiServiceTestCase { eq(true)); verify(mCallback, never()).removeAutoGroup(anyString()); verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString()); - verify(mCallback, times(numChildren - 1)).updateAutogroupSummary(anyInt(), anyString(), - anyString(), any()); + verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(), + any()); verify(mCallback, times(numChildren)).removeAppProvidedSummaryOnClassification(anyString(), anyString()); } @Test + @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, + FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION, + FLAG_NOTIFICATION_CLASSIFICATION}) + public void testUnbundleNotification_originalSummaryMissing_autogroupInNewSection() { + // Check that unbundled notifications are moved to the original section and aggregated + // with existing autogrouped notifications + final List<NotificationRecord> notificationList = new ArrayList<>(); + final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>(); + final String pkg = "package"; + + final int summaryId = 0; + final int numChildren = AUTOGROUP_AT_COUNT - 1; + // Post a regular/valid group: summary + notifications (one less than autogroup limit) + NotificationRecord summary = getNotificationRecord(pkg, summaryId, + String.valueOf(summaryId), UserHandle.SYSTEM, "testGrp", true); + notificationList.add(summary); + summaryByGroup.put(summary.getGroupKey(), summary); + final String originalAppGroupKey = summary.getGroupKey(); + final NotificationChannel originalChannel = summary.getChannel(); + for (int i = 0; i < numChildren; i++) { + NotificationRecord child = getNotificationRecord(pkg, i + 42, String.valueOf(i + 42), + UserHandle.SYSTEM, "testGrp", false); + notificationList.add(child); + mGroupHelper.onNotificationPostedWithDelay(child, notificationList, summaryByGroup); + } + + // Classify/bundle all child notifications: original group & summary is removed + final NotificationChannel socialChannel = new NotificationChannel( + NotificationChannel.SOCIAL_MEDIA_ID, NotificationChannel.SOCIAL_MEDIA_ID, + IMPORTANCE_DEFAULT); + for (NotificationRecord record: notificationList) { + if (record.getOriginalGroupKey().contains("testGrp") + && record.getNotification().isGroupChild()) { + record.updateNotificationChannel(socialChannel); + mGroupHelper.onChannelUpdated(record); + } + } + + // Check that no autogroup summaries were created for the social section + verify(mCallback, never()).addAutoGroupSummary(anyInt(), anyString(), anyString(), + anyString(), anyInt(), any()); + verify(mCallback, never()).addAutoGroup(anyString(), anyString(), anyBoolean()); + verify(mCallback, never()).removeAutoGroup(anyString()); + verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString()); + verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(), + any()); + verify(mCallback, times(numChildren)).removeAppProvidedSummaryOnClassification( + anyString(), eq(originalAppGroupKey)); + + // Cancel summary + summary.isCanceled = true; + summaryByGroup.clear(); + notificationList.remove(summary); + + // Add 1 ungrouped notification in the original section + NotificationRecord ungroupedNotification = getNotificationRecord(pkg, 4242, + String.valueOf(4242), UserHandle.SYSTEM); + notificationList.add(ungroupedNotification); + mGroupHelper.onNotificationPosted(ungroupedNotification, false); + + // Unbundle the bundled notifications => notifications are moved back to the original group + // and an aggregate group is created because autogroup limit is reached + reset(mCallback); + for (NotificationRecord record: notificationList) { + if (record.getNotification().isGroupChild() + && record.getOriginalGroupKey().contains("testGrp") + && NotificationChannel.SYSTEM_RESERVED_IDS.contains( + record.getChannel().getId())) { + record.updateNotificationChannel(originalChannel); + mGroupHelper.onNotificationUnbundled(record, false); + } + } + + // Check that a new aggregate group is created + final String expectedGroupKey_alerting = GroupHelper.getFullAggregateGroupKey(pkg, + AGGREGATE_GROUP_KEY + "AlertingSection", UserHandle.SYSTEM.getIdentifier()); + verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(), + eq(expectedGroupKey_alerting), anyInt(), any()); + verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(), + eq(expectedGroupKey_alerting), eq(true)); + verify(mCallback, never()).removeAutoGroup(anyString()); + verify(mCallback, times(numChildren)).removeAutoGroupSummary(anyInt(), anyString(), + anyString()); + verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(), + any()); + } + + @Test + @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, + FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION, + FLAG_NOTIFICATION_CLASSIFICATION}) + public void testUnbundleNotification_originalSummaryExists() { + // Check that unbundled notifications are moved to the original section and original group + // when the original summary is still present + final List<NotificationRecord> notificationList = new ArrayList<>(); + final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>(); + final String pkg = "package"; + + final int summaryId = 0; + final int numChildren = AUTOGROUP_AT_COUNT + 1; + // Post a regular/valid group: summary + notifications + NotificationRecord summary = getNotificationRecord(pkg, summaryId, + String.valueOf(summaryId), UserHandle.SYSTEM, "testGrp", true); + notificationList.add(summary); + summaryByGroup.put(summary.getGroupKey(), summary); + final String originalAppGroupKey = summary.getGroupKey(); + final NotificationChannel originalChannel = summary.getChannel(); + for (int i = 0; i < numChildren; i++) { + NotificationRecord child = getNotificationRecord(pkg, i + 42, String.valueOf(i + 42), + UserHandle.SYSTEM, "testGrp", false); + notificationList.add(child); + mGroupHelper.onNotificationPostedWithDelay(child, notificationList, summaryByGroup); + } + + // Classify/bundle child notifications: all except one, to keep the original group + final NotificationChannel socialChannel = new NotificationChannel( + NotificationChannel.SOCIAL_MEDIA_ID, NotificationChannel.SOCIAL_MEDIA_ID, + IMPORTANCE_DEFAULT); + final String expectedGroupKey_social = GroupHelper.getFullAggregateGroupKey(pkg, + AGGREGATE_GROUP_KEY + "SocialSection", UserHandle.SYSTEM.getIdentifier()); + final NotificationAttributes expectedSummaryAttr_social = new NotificationAttributes( + BASE_FLAGS, mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT, + NotificationChannel.SOCIAL_MEDIA_ID); + int numChildrenBundled = 0; + for (NotificationRecord record: notificationList) { + if (record.getOriginalGroupKey().contains("testGrp") + && record.getNotification().isGroupChild()) { + record.updateNotificationChannel(socialChannel); + mGroupHelper.onChannelUpdated(record); + numChildrenBundled++; + if (numChildrenBundled == AUTOGROUP_AT_COUNT) { + break; + } + } + } + + // Check that 1 autogroup summaries were created for the social section + verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(), + eq(expectedGroupKey_social), anyInt(), eq(expectedSummaryAttr_social)); + verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(), + eq(expectedGroupKey_social), eq(true)); + verify(mCallback, never()).removeAutoGroup(anyString()); + verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString()); + verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(), + any()); + verify(mCallback, times(AUTOGROUP_AT_COUNT)).removeAppProvidedSummaryOnClassification( + anyString(), eq(originalAppGroupKey)); + + // Adjust group key and cancel summaries + for (NotificationRecord record: notificationList) { + if (record.getNotification().isGroupSummary()) { + record.isCanceled = true; + } else if (record.getOriginalGroupKey().contains("testGrp") + && NotificationChannel.SYSTEM_RESERVED_IDS.contains( + record.getChannel().getId())) { + record.setOverrideGroupKey(expectedGroupKey_social); + } + } + + // Add 1 ungrouped notification in the original section + NotificationRecord ungroupedNotification = getNotificationRecord(pkg, 4242, + String.valueOf(4242), UserHandle.SYSTEM); + notificationList.add(ungroupedNotification); + mGroupHelper.onNotificationPosted(ungroupedNotification, false); + + // Unbundle the bundled notifications => social section summary is destroyed + // and notifications are moved back to the original group + reset(mCallback); + for (NotificationRecord record: notificationList) { + if (record.getNotification().isGroupChild() + && record.getOriginalGroupKey().contains("testGrp") + && NotificationChannel.SYSTEM_RESERVED_IDS.contains( + record.getChannel().getId())) { + record.updateNotificationChannel(originalChannel); + mGroupHelper.onNotificationUnbundled(record, true); + } + } + + // Check that the autogroup summary for the social section was removed + // and that no new autogroup summaries were created + verify(mCallback, never()).addAutoGroupSummary(anyInt(), anyString(), anyString(), + anyString(), anyInt(), any()); + verify(mCallback, never()).addAutoGroup(anyString(), anyString(), anyBoolean()); + verify(mCallback, never()).removeAutoGroup(anyString()); + verify(mCallback, times(1)).removeAutoGroupSummary(anyInt(), eq(pkg), + eq(expectedGroupKey_social)); + verify(mCallback, times(AUTOGROUP_AT_COUNT - 1)).updateAutogroupSummary(anyInt(), eq(pkg), + eq(expectedGroupKey_social), any()); + + for (NotificationRecord record: notificationList) { + if (record.getNotification().isGroupChild() + && record.getOriginalGroupKey().contains("testGrp")) { + assertThat(record.getSbn().getOverrideGroupKey()).isNull(); + } + } + } + + @Test @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING) public void testMoveAggregateGroups_updateChannel_groupsUngrouped() { final String pkg = "package"; |