• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.server.notification;
17 
18 import static android.app.Flags.FLAG_SORT_SECTION_BY_TIME;
19 import static android.app.Notification.COLOR_DEFAULT;
20 import static android.app.Notification.FLAG_AUTO_CANCEL;
21 import static android.app.Notification.FLAG_BUBBLE;
22 import static android.app.Notification.FLAG_CAN_COLORIZE;
23 import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
24 import static android.app.Notification.FLAG_GROUP_SUMMARY;
25 import static android.app.Notification.FLAG_NO_CLEAR;
26 import static android.app.Notification.FLAG_ONGOING_EVENT;
27 import static android.app.Notification.GROUP_ALERT_ALL;
28 import static android.app.Notification.GROUP_ALERT_CHILDREN;
29 import static android.app.Notification.GROUP_ALERT_SUMMARY;
30 import static android.app.Notification.VISIBILITY_PRIVATE;
31 import static android.app.Notification.VISIBILITY_PUBLIC;
32 import static android.app.Notification.VISIBILITY_SECRET;
33 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
34 import static android.app.NotificationManager.IMPORTANCE_LOW;
35 import static android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION;
36 import static android.service.notification.Flags.FLAG_NOTIFICATION_FORCE_GROUPING;
37 import static android.service.notification.Flags.FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION;
38 import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
39 import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
40 
41 import static com.android.server.notification.Flags.FLAG_NOTIFICATION_FORCE_GROUP_CONVERSATIONS;
42 import static com.android.server.notification.Flags.FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS;
43 import static com.android.server.notification.GroupHelper.AGGREGATE_GROUP_KEY;
44 import static com.android.server.notification.GroupHelper.AUTOGROUP_KEY;
45 import static com.android.server.notification.GroupHelper.BASE_FLAGS;
46 
47 import static com.google.common.truth.Truth.assertThat;
48 
49 import static junit.framework.Assert.assertEquals;
50 
51 import static org.mockito.ArgumentMatchers.any;
52 import static org.mockito.ArgumentMatchers.anyBoolean;
53 import static org.mockito.ArgumentMatchers.anyInt;
54 import static org.mockito.ArgumentMatchers.anyString;
55 import static org.mockito.ArgumentMatchers.eq;
56 import static org.mockito.Mockito.doReturn;
57 import static org.mockito.Mockito.mock;
58 import static org.mockito.Mockito.never;
59 import static org.mockito.Mockito.reset;
60 import static org.mockito.Mockito.spy;
61 import static org.mockito.Mockito.times;
62 import static org.mockito.Mockito.verify;
63 import static org.mockito.Mockito.verifyNoMoreInteractions;
64 import static org.mockito.Mockito.when;
65 
66 import android.annotation.SuppressLint;
67 import android.app.Notification;
68 import android.app.NotificationChannel;
69 import android.app.PendingIntent;
70 import android.content.Intent;
71 import android.content.pm.PackageManager;
72 import android.graphics.Color;
73 import android.graphics.drawable.AdaptiveIconDrawable;
74 import android.graphics.drawable.Drawable;
75 import android.graphics.drawable.Icon;
76 import android.os.UserHandle;
77 import android.platform.test.annotations.DisableFlags;
78 import android.platform.test.annotations.EnableFlags;
79 import android.platform.test.flag.junit.FlagsParameterization;
80 import android.platform.test.flag.junit.SetFlagsRule;
81 import android.service.notification.StatusBarNotification;
82 import android.util.ArrayMap;
83 
84 import androidx.test.filters.SmallTest;
85 
86 import com.android.internal.R;
87 import com.android.server.UiServiceTestCase;
88 import com.android.server.notification.GroupHelper.CachedSummary;
89 import com.android.server.notification.GroupHelper.FullyQualifiedGroupKey;
90 import com.android.server.notification.GroupHelper.NotificationAttributes;
91 import com.android.server.notification.GroupHelper.NotificationSectioner;
92 
93 import org.junit.Before;
94 import org.junit.Rule;
95 import org.junit.Test;
96 import org.junit.runner.RunWith;
97 import org.mockito.Mock;
98 import org.mockito.Mockito;
99 import org.mockito.MockitoAnnotations;
100 
101 import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
102 import platform.test.runner.parameterized.Parameters;
103 
104 import java.util.ArrayList;
105 import java.util.HashMap;
106 import java.util.List;
107 import java.util.Map;
108 
109 @SmallTest
110 @SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the class.
111 @RunWith(ParameterizedAndroidJunit4.class)
112 public class GroupHelperTest extends UiServiceTestCase {
113     @Rule
114     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
115 
116     private final int DEFAULT_VISIBILITY = VISIBILITY_PRIVATE;
117     private final int DEFAULT_GROUP_ALERT = GROUP_ALERT_CHILDREN;
118 
119     private final String TEST_CHANNEL_ID = "TEST_CHANNEL_ID";
120 
121     private @Mock GroupHelper.Callback mCallback;
122     private @Mock PackageManager mPackageManager;
123 
124     private final static int AUTOGROUP_AT_COUNT = 7;
125     private final static int AUTOGROUP_SINGLETONS_AT_COUNT = 2;
126     private GroupHelper mGroupHelper;
127     private @Mock Icon mSmallIcon;
128 
129     @Parameters(name = "{0}")
getParams()130     public static List<FlagsParameterization> getParams() {
131         return FlagsParameterization.allCombinationsOf(
132                 android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST);
133     }
134 
GroupHelperTest(FlagsParameterization flags)135     public GroupHelperTest(FlagsParameterization flags) {
136         mSetFlagsRule.setFlagsParameterization(flags);
137     }
138 
139     @Before
setUp()140     public void setUp() {
141         MockitoAnnotations.initMocks(this);
142 
143         mGroupHelper = new GroupHelper(getContext(), mPackageManager, AUTOGROUP_AT_COUNT,
144                 AUTOGROUP_SINGLETONS_AT_COUNT, mCallback);
145 
146         NotificationRecord r = mock(NotificationRecord.class);
147         StatusBarNotification sbn = getSbn("package", 0, "0", UserHandle.SYSTEM);
148         when(r.getNotification()).thenReturn(sbn.getNotification());
149         when(r.getSbn()).thenReturn(sbn);
150         when(mSmallIcon.sameAs(mSmallIcon)).thenReturn(true);
151     }
152 
getSbn(String pkg, int id, String tag, UserHandle user, String groupKey, Icon smallIcon, int iconColor)153     private StatusBarNotification getSbn(String pkg, int id, String tag,
154             UserHandle user, String groupKey, Icon smallIcon, int iconColor) {
155         Notification.Builder nb = new Notification.Builder(getContext(), TEST_CHANNEL_ID)
156                 .setContentTitle("A")
157                 .setWhen(1205)
158                 .setSmallIcon(smallIcon)
159                 .setColor(iconColor);
160         if (groupKey != null) {
161             nb.setGroup(groupKey);
162         }
163         return new StatusBarNotification(pkg, pkg, id, tag, 0, 0, nb.build(), user, null,
164                 System.currentTimeMillis());
165     }
166 
getSbn(String pkg, int id, String tag, UserHandle user, String groupKey)167     private StatusBarNotification getSbn(String pkg, int id, String tag,
168             UserHandle user, String groupKey) {
169         return getSbn(pkg, id, tag, user, groupKey, mSmallIcon, Notification.COLOR_DEFAULT);
170     }
171 
getSbn(String pkg, int id, String tag, UserHandle user)172     private StatusBarNotification getSbn(String pkg, int id, String tag,
173             UserHandle user) {
174         return getSbn(pkg, id, tag, user, null);
175     }
176 
getNotificationRecord(String pkg, int id, String tag, UserHandle user)177     private NotificationRecord getNotificationRecord(String pkg, int id, String tag,
178             UserHandle user) {
179         return getNotificationRecord(pkg, id, tag, user, null, false);
180     }
181 
getNotificationRecord(String pkg, int id, String tag, UserHandle user, String groupKey, boolean isSummary)182     private NotificationRecord getNotificationRecord(String pkg, int id, String tag,
183             UserHandle user, String groupKey, boolean isSummary) {
184         return getNotificationRecord(pkg, id, tag, user, groupKey, isSummary, IMPORTANCE_DEFAULT);
185     }
186 
getNotificationRecord(String pkg, int id, String tag, UserHandle user, String groupKey, boolean isSummary, int importance)187     private NotificationRecord getNotificationRecord(String pkg, int id, String tag,
188             UserHandle user, String groupKey, boolean isSummary, int importance) {
189         return getNotificationRecord(pkg, id, tag, user, groupKey, isSummary,
190                 new NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_ID, importance));
191     }
192 
getNotificationRecord(String pkg, int id, String tag, UserHandle user, String groupKey, boolean isSummary, NotificationChannel channel)193     private NotificationRecord getNotificationRecord(String pkg, int id, String tag,
194             UserHandle user, String groupKey, boolean isSummary, NotificationChannel channel) {
195         StatusBarNotification sbn = getSbn(pkg, id, tag, user, groupKey);
196         if (isSummary) {
197             sbn.getNotification().flags |= FLAG_GROUP_SUMMARY;
198         }
199         return new NotificationRecord(getContext(), sbn, channel);
200     }
201 
getNotificationRecord(StatusBarNotification sbn)202     private NotificationRecord getNotificationRecord(StatusBarNotification sbn) {
203         return new NotificationRecord(getContext(), sbn,
204             new NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_ID, IMPORTANCE_DEFAULT));
205     }
206 
getNotificationAttributes(int flags)207     private NotificationAttributes getNotificationAttributes(int flags) {
208         return new NotificationAttributes(flags, mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY,
209                 DEFAULT_GROUP_ALERT, TEST_CHANNEL_ID);
210     }
211 
getExpectedAutogroupKey(final NotificationRecord record)212     private String getExpectedAutogroupKey(final NotificationRecord record) {
213         if (android.service.notification.Flags.notificationForceGrouping()) {
214             return GroupHelper.getFullAggregateGroupKey(record);
215         } else {
216             return AUTOGROUP_KEY;
217         }
218     }
219 
220     @Test
testGetAutogroupSummaryFlags_noChildren()221     public void testGetAutogroupSummaryFlags_noChildren() {
222         ArrayMap<String, NotificationAttributes> children = new ArrayMap<>();
223 
224         assertEquals(BASE_FLAGS, GroupHelper.getAutogroupSummaryFlags(children));
225     }
226 
227     @Test
testGetAutogroupSummaryFlags_oneOngoing()228     public void testGetAutogroupSummaryFlags_oneOngoing() {
229         ArrayMap<String, NotificationAttributes> children = new ArrayMap<>();
230         children.put("a", getNotificationAttributes(0));
231         children.put("b", getNotificationAttributes(FLAG_ONGOING_EVENT));
232         children.put("c", getNotificationAttributes(FLAG_BUBBLE));
233 
234         assertEquals(FLAG_ONGOING_EVENT | BASE_FLAGS,
235                 GroupHelper.getAutogroupSummaryFlags(children));
236     }
237 
238     @Test
testGetAutogroupSummaryFlags_oneOngoingNoClear()239     public void testGetAutogroupSummaryFlags_oneOngoingNoClear() {
240         ArrayMap<String, NotificationAttributes> children = new ArrayMap<>();
241         children.put("a", getNotificationAttributes(0));
242         children.put("b", getNotificationAttributes(FLAG_ONGOING_EVENT | FLAG_NO_CLEAR));
243         children.put("c", getNotificationAttributes(FLAG_BUBBLE));
244 
245         assertEquals(FLAG_NO_CLEAR | FLAG_ONGOING_EVENT | BASE_FLAGS,
246                 GroupHelper.getAutogroupSummaryFlags(children));
247     }
248 
249     @Test
testGetAutogroupSummaryFlags_oneOngoingBubble()250     public void testGetAutogroupSummaryFlags_oneOngoingBubble() {
251         ArrayMap<String, NotificationAttributes> children = new ArrayMap<>();
252         children.put("a", getNotificationAttributes(0));
253         children.put("b", getNotificationAttributes(FLAG_ONGOING_EVENT | FLAG_BUBBLE));
254         children.put("c", getNotificationAttributes(FLAG_BUBBLE));
255 
256         assertEquals(FLAG_ONGOING_EVENT | BASE_FLAGS,
257                 GroupHelper.getAutogroupSummaryFlags(children));
258     }
259 
260     @Test
testGetAutogroupSummaryFlags_multipleOngoing()261     public void testGetAutogroupSummaryFlags_multipleOngoing() {
262         ArrayMap<String, NotificationAttributes> children = new ArrayMap<>();
263         children.put("a", getNotificationAttributes(0));
264         children.put("b", getNotificationAttributes(FLAG_ONGOING_EVENT));
265         children.put("c", getNotificationAttributes(FLAG_BUBBLE));
266         children.put("d", getNotificationAttributes(FLAG_ONGOING_EVENT));
267 
268         assertEquals(FLAG_ONGOING_EVENT | BASE_FLAGS,
269                 GroupHelper.getAutogroupSummaryFlags(children));
270     }
271 
272     @Test
testGetAutogroupSummaryFlags_oneAutoCancel()273     public void testGetAutogroupSummaryFlags_oneAutoCancel() {
274         ArrayMap<String, NotificationAttributes> children = new ArrayMap<>();
275         children.put("a", getNotificationAttributes(0));
276         children.put("b", getNotificationAttributes(FLAG_AUTO_CANCEL));
277         children.put("c", getNotificationAttributes(FLAG_BUBBLE));
278 
279         assertEquals(BASE_FLAGS,
280                 GroupHelper.getAutogroupSummaryFlags(children));
281     }
282 
283     @Test
testGetAutogroupSummaryFlags_allAutoCancel()284     public void testGetAutogroupSummaryFlags_allAutoCancel() {
285         ArrayMap<String, NotificationAttributes> children = new ArrayMap<>();
286         children.put("a", getNotificationAttributes(FLAG_AUTO_CANCEL));
287         children.put("b", getNotificationAttributes(FLAG_AUTO_CANCEL | FLAG_CAN_COLORIZE));
288         children.put("c", getNotificationAttributes(FLAG_AUTO_CANCEL));
289         children.put("d", getNotificationAttributes(FLAG_AUTO_CANCEL | FLAG_FOREGROUND_SERVICE));
290 
291         assertEquals(FLAG_AUTO_CANCEL | BASE_FLAGS,
292                 GroupHelper.getAutogroupSummaryFlags(children));
293     }
294 
295     @Test
testGetAutogroupSummaryFlags_allAutoCancelOneOngoing()296     public void testGetAutogroupSummaryFlags_allAutoCancelOneOngoing() {
297         ArrayMap<String, NotificationAttributes> children = new ArrayMap<>();
298         children.put("a", getNotificationAttributes(FLAG_AUTO_CANCEL));
299         children.put("b", getNotificationAttributes(FLAG_AUTO_CANCEL | FLAG_CAN_COLORIZE));
300         children.put("c", getNotificationAttributes(FLAG_AUTO_CANCEL));
301         children.put("d", getNotificationAttributes(
302                 FLAG_AUTO_CANCEL | FLAG_FOREGROUND_SERVICE | FLAG_ONGOING_EVENT));
303 
304         assertEquals(FLAG_AUTO_CANCEL| FLAG_ONGOING_EVENT | BASE_FLAGS,
305                 GroupHelper.getAutogroupSummaryFlags(children));
306     }
307 
308     @Test
testNoGroup_postingUnderLimit()309     public void testNoGroup_postingUnderLimit() {
310         final String pkg = "package";
311         for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
312             mGroupHelper.onNotificationPosted(
313                 getNotificationRecord(pkg, i, String.valueOf(i), UserHandle.SYSTEM),
314                 false);
315         }
316         verifyNoMoreInteractions(mCallback);
317     }
318 
319     @Test
testNoGroup_multiPackage()320     public void testNoGroup_multiPackage() {
321         final String pkg = "package";
322         final String pkg2 = "package2";
323         for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
324             mGroupHelper.onNotificationPosted(
325                 getNotificationRecord(pkg, i, String.valueOf(i), UserHandle.SYSTEM),
326                 false);
327         }
328         mGroupHelper.onNotificationPosted(
329             getNotificationRecord(pkg2, AUTOGROUP_AT_COUNT, "four", UserHandle.SYSTEM), false);
330         verifyNoMoreInteractions(mCallback);
331     }
332 
333     @Test
testNoGroup_multiUser()334     public void testNoGroup_multiUser() {
335         final String pkg = "package";
336         for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
337             mGroupHelper.onNotificationPosted(
338                 getNotificationRecord(pkg, i, String.valueOf(i), UserHandle.SYSTEM),
339                 false);
340         }
341         mGroupHelper.onNotificationPosted(
342             getNotificationRecord(pkg, AUTOGROUP_AT_COUNT, "four", UserHandle.of(7)), false);
343         verifyNoMoreInteractions(mCallback);
344     }
345 
346     @Test
testNoGroup_someAreGrouped()347     public void testNoGroup_someAreGrouped() {
348         final String pkg = "package";
349         for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
350             mGroupHelper.onNotificationPosted(
351                 getNotificationRecord(pkg, i, String.valueOf(i), UserHandle.SYSTEM), false);
352         }
353         mGroupHelper.onNotificationPosted(
354             getNotificationRecord(pkg, AUTOGROUP_AT_COUNT, "four", UserHandle.SYSTEM, "a", false),
355             false);
356         verifyNoMoreInteractions(mCallback);
357     }
358 
359     @Test
360     @DisableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
testAddSummary_alwaysAutogroup()361     public void testAddSummary_alwaysAutogroup() {
362         final String pkg = "package";
363         final String autogroupKey = getExpectedAutogroupKey(
364                 getNotificationRecord(pkg, 0, String.valueOf(0), UserHandle.SYSTEM));
365         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
366             mGroupHelper.onNotificationPosted(
367                 getNotificationRecord(pkg, i, String.valueOf(i), UserHandle.SYSTEM), false);
368         }
369         verify(mCallback, times(1)).addAutoGroupSummary(
370                 anyInt(), eq(pkg), anyString(), eq(autogroupKey),
371                 anyInt(), eq(getNotificationAttributes(BASE_FLAGS)));
372         verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(), eq(autogroupKey),
373                 anyBoolean());
374         verify(mCallback, never()).removeAutoGroup(anyString());
375         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
376         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
377                 any());
378     }
379 
380     @Test
381     @EnableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
testAddSummary()382     public void testAddSummary() {
383         final String pkg = "package";
384         final String autogroupKey = getExpectedAutogroupKey(
385                 getNotificationRecord(pkg, 0, String.valueOf(0), UserHandle.SYSTEM));
386         for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
387             assertThat(mGroupHelper.onNotificationPosted(
388                     getNotificationRecord(pkg, i, String.valueOf(i), UserHandle.SYSTEM),
389                     false)).isFalse();
390         }
391         assertThat(mGroupHelper.onNotificationPosted(
392             getNotificationRecord(pkg, AUTOGROUP_AT_COUNT - 1, String.valueOf(AUTOGROUP_AT_COUNT - 1),
393                         UserHandle.SYSTEM), false)).isTrue();
394         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
395                 eq(autogroupKey), anyInt(), eq(getNotificationAttributes(BASE_FLAGS)));
396         verify(mCallback, times(AUTOGROUP_AT_COUNT - 1)).addAutoGroup(anyString(), anyString(),
397                 anyBoolean());
398         verify(mCallback, never()).removeAutoGroup(anyString());
399         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
400         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
401                 any());
402     }
403 
404     @Test
405     @DisableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
testAddSummary_oneChildOngoing_summaryOngoing_alwaysAutogroup()406     public void testAddSummary_oneChildOngoing_summaryOngoing_alwaysAutogroup() {
407         final String pkg = "package";
408         final String autogroupKey = getExpectedAutogroupKey(
409                 getNotificationRecord(pkg, 0, String.valueOf(0), UserHandle.SYSTEM));
410         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
411             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
412                     UserHandle.SYSTEM);
413             if (i == 0) {
414                 r.getNotification().flags |= FLAG_ONGOING_EVENT;
415             }
416             mGroupHelper.onNotificationPosted(r, false);
417         }
418         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
419                 eq(autogroupKey), anyInt(),
420                 eq(getNotificationAttributes(BASE_FLAGS | FLAG_ONGOING_EVENT)));
421         verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(), anyString(),
422                 anyBoolean());
423         verify(mCallback, never()).removeAutoGroup(anyString());
424         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
425         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
426                 any());
427     }
428 
429     @Test
430     @EnableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
testAddSummary_oneChildOngoing_summaryOngoing()431     public void testAddSummary_oneChildOngoing_summaryOngoing() {
432         final String pkg = "package";
433         final String autogroupKey = getExpectedAutogroupKey(
434                 getNotificationRecord(pkg, 0, String.valueOf(0), UserHandle.SYSTEM));
435         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
436             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
437                     UserHandle.SYSTEM);
438             if (i == 0) {
439                 r.getNotification().flags |= FLAG_ONGOING_EVENT;
440             }
441             mGroupHelper.onNotificationPosted(r, false);
442         }
443         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
444                 eq(autogroupKey), anyInt(),
445                 eq(getNotificationAttributes(BASE_FLAGS | FLAG_ONGOING_EVENT)));
446         verify(mCallback, times(AUTOGROUP_AT_COUNT - 1)).addAutoGroup(anyString(), anyString(),
447                 anyBoolean());
448         verify(mCallback, never()).removeAutoGroup(anyString());
449         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
450         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
451                 any());
452     }
453 
454     @Test
455     @DisableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
testAddSummary_oneChildAutoCancel_summaryNotAutoCancel_alwaysAutogroup()456     public void testAddSummary_oneChildAutoCancel_summaryNotAutoCancel_alwaysAutogroup() {
457         final String pkg = "package";
458         final String autogroupKey = getExpectedAutogroupKey(
459             getNotificationRecord(pkg, 0, String.valueOf(0), UserHandle.SYSTEM));
460         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
461             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
462                     UserHandle.SYSTEM);
463             if (i == 0) {
464                 r.getNotification().flags |= FLAG_AUTO_CANCEL;
465             }
466             mGroupHelper.onNotificationPosted(r, false);
467         }
468         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
469                 eq(autogroupKey), anyInt(), eq(getNotificationAttributes(BASE_FLAGS)));
470         verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(), anyString(),
471                 anyBoolean());
472         verify(mCallback, never()).removeAutoGroup(anyString());
473         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
474         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
475                 any());
476     }
477 
478     @Test
479     @EnableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
testAddSummary_oneChildAutoCancel_summaryNotAutoCancel()480     public void testAddSummary_oneChildAutoCancel_summaryNotAutoCancel() {
481         final String pkg = "package";
482         final String autogroupKey = getExpectedAutogroupKey(
483                 getNotificationRecord(pkg, 0, String.valueOf(0), UserHandle.SYSTEM));
484         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
485             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
486                     UserHandle.SYSTEM);
487             if (i == 0) {
488                 r.getNotification().flags |= FLAG_AUTO_CANCEL;
489             }
490             mGroupHelper.onNotificationPosted(r, false);
491         }
492         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
493                 eq(autogroupKey), anyInt(), eq(getNotificationAttributes(BASE_FLAGS)));
494         verify(mCallback, times(AUTOGROUP_AT_COUNT - 1)).addAutoGroup(anyString(),
495                 eq(autogroupKey), anyBoolean());
496         verify(mCallback, never()).removeAutoGroup(anyString());
497         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
498         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
499                 any());
500     }
501 
502     @Test
503     @DisableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
testAddSummary_allChildrenAutoCancel_summaryAutoCancel_alwaysAutogroup()504     public void testAddSummary_allChildrenAutoCancel_summaryAutoCancel_alwaysAutogroup() {
505         final String pkg = "package";
506         final String autogroupKey = getExpectedAutogroupKey(
507                 getNotificationRecord(pkg, 0, String.valueOf(0), UserHandle.SYSTEM));
508         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
509             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
510                     UserHandle.SYSTEM);
511             r.getNotification().flags |= FLAG_AUTO_CANCEL;
512             mGroupHelper.onNotificationPosted(r, false);
513         }
514         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
515                 eq(autogroupKey), anyInt(),
516                 eq(getNotificationAttributes(BASE_FLAGS | FLAG_AUTO_CANCEL)));
517         verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(), eq(autogroupKey),
518                 anyBoolean());
519         verify(mCallback, never()).removeAutoGroup(anyString());
520         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
521         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
522                 any());
523     }
524 
525     @Test
526     @EnableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
testAddSummary_allChildrenAutoCancel_summaryAutoCancel()527     public void testAddSummary_allChildrenAutoCancel_summaryAutoCancel() {
528         final String pkg = "package";
529         final String autogroupKey = getExpectedAutogroupKey(
530                 getNotificationRecord(pkg, 0, String.valueOf(0), UserHandle.SYSTEM));
531         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
532             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
533                     UserHandle.SYSTEM);
534             r.getNotification().flags |= FLAG_AUTO_CANCEL;
535             mGroupHelper.onNotificationPosted(r, false);
536         }
537         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
538                 eq(autogroupKey), anyInt(),
539                 eq(getNotificationAttributes(BASE_FLAGS | FLAG_AUTO_CANCEL)));
540         verify(mCallback, times(AUTOGROUP_AT_COUNT - 1)).addAutoGroup(anyString(),
541                 eq(autogroupKey), anyBoolean());
542         verify(mCallback, never()).removeAutoGroup(anyString());
543         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
544         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
545                 any());
546     }
547 
548     @Test
549     @DisableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
testAddSummary_summaryAutoCancelNoClear_alwaysAutogroup()550     public void testAddSummary_summaryAutoCancelNoClear_alwaysAutogroup() {
551         final String pkg = "package";
552         final String autogroupKey = getExpectedAutogroupKey(
553                 getNotificationRecord(pkg, 0, String.valueOf(0), UserHandle.SYSTEM));
554         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
555             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
556                     UserHandle.SYSTEM);
557             r.getNotification().flags |= FLAG_AUTO_CANCEL;
558             if (i == 0) {
559                 r.getNotification().flags |= FLAG_NO_CLEAR;
560             }
561             mGroupHelper.onNotificationPosted(r, false);
562         }
563         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
564                 eq(autogroupKey), anyInt(),
565                 eq(getNotificationAttributes(BASE_FLAGS | FLAG_AUTO_CANCEL | FLAG_NO_CLEAR)));
566         verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(), eq(autogroupKey),
567                 anyBoolean());
568         verify(mCallback, never()).removeAutoGroup(anyString());
569         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
570         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
571                 any());
572     }
573 
574     @Test
575     @EnableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
testAddSummary_summaryAutoCancelNoClear()576     public void testAddSummary_summaryAutoCancelNoClear() {
577         final String pkg = "package";
578         final String autogroupKey = getExpectedAutogroupKey(
579                 getNotificationRecord(pkg, 0, String.valueOf(0), UserHandle.SYSTEM));
580         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
581             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
582                     UserHandle.SYSTEM);
583             r.getNotification().flags |= FLAG_AUTO_CANCEL;
584             if (i == 0) {
585                 r.getNotification().flags |= FLAG_NO_CLEAR;
586             }
587             mGroupHelper.onNotificationPosted(r, false);
588         }
589         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
590                 eq(autogroupKey), anyInt(),
591                 eq(getNotificationAttributes(BASE_FLAGS | FLAG_AUTO_CANCEL | FLAG_NO_CLEAR)));
592         verify(mCallback, times(AUTOGROUP_AT_COUNT - 1)).addAutoGroup(anyString(),
593                 eq(autogroupKey), anyBoolean());
594         verify(mCallback, never()).removeAutoGroup(anyString());
595         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
596         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
597                 any());
598     }
599 
600     @Test
testAutoGrouped_allOngoing_updateChildNotOngoing()601     public void testAutoGrouped_allOngoing_updateChildNotOngoing() {
602         final String pkg = "package";
603 
604         // Post AUTOGROUP_AT_COUNT ongoing notifications
605         ArrayList<NotificationRecord> notifications = new ArrayList<>();
606         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
607             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
608                     UserHandle.SYSTEM);
609             r.getNotification().flags |= FLAG_ONGOING_EVENT;
610             notifications.add(r);
611         }
612 
613         for (NotificationRecord r: notifications) {
614             mGroupHelper.onNotificationPosted(r, false);
615         }
616 
617         // One notification is no longer ongoing
618         notifications.get(0).getNotification().flags &= ~FLAG_ONGOING_EVENT;
619         mGroupHelper.onNotificationPosted(notifications.get(0), true);
620 
621         // Summary should keep FLAG_ONGOING_EVENT if any child has it
622         verify(mCallback).updateAutogroupSummary(anyInt(), anyString(), anyString(),
623                 eq(getNotificationAttributes(BASE_FLAGS | FLAG_ONGOING_EVENT)));
624     }
625 
626     @Test
627     @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
testAutoGrouped_singleOngoing_removeOngoingChild()628     public void testAutoGrouped_singleOngoing_removeOngoingChild() {
629         final String pkg = "package";
630 
631         // Post AUTOGROUP_AT_COUNT ongoing notifications
632         ArrayList<NotificationRecord> notifications = new ArrayList<>();
633         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
634             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
635                     UserHandle.SYSTEM);
636             if (i == 0) {
637                 r.getNotification().flags |= FLAG_ONGOING_EVENT;
638             }
639             notifications.add(r);
640         }
641 
642         for (NotificationRecord r: notifications) {
643             mGroupHelper.onNotificationPosted(r, false);
644         }
645 
646         // remove ongoing
647         mGroupHelper.onNotificationRemoved(notifications.get(0), new ArrayList<>(), false);
648 
649         // Summary is no longer ongoing
650         verify(mCallback).updateAutogroupSummary(anyInt(), anyString(), anyString(),
651                 eq(getNotificationAttributes(BASE_FLAGS)));
652     }
653 
654     @Test
testAutoGrouped_noOngoing_updateOngoingChild()655     public void testAutoGrouped_noOngoing_updateOngoingChild() {
656         final String pkg = "package";
657 
658         // Post AUTOGROUP_AT_COUNT ongoing notifications
659         ArrayList<NotificationRecord> notifications = new ArrayList<>();
660         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
661             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
662                     UserHandle.SYSTEM);
663             notifications.add(r);
664         }
665 
666         for (NotificationRecord r: notifications) {
667             mGroupHelper.onNotificationPosted(r, false);
668         }
669 
670         // update to ongoing
671         notifications.get(0).getNotification().flags |= FLAG_ONGOING_EVENT;
672         mGroupHelper.onNotificationPosted(notifications.get(0), true);
673 
674         // Summary is now ongoing
675         verify(mCallback).updateAutogroupSummary(anyInt(), anyString(), anyString(),
676                 eq(getNotificationAttributes(BASE_FLAGS | FLAG_ONGOING_EVENT)));
677     }
678 
679     @Test
testAutoGrouped_noOngoing_addOngoingChild()680     public void testAutoGrouped_noOngoing_addOngoingChild() {
681         final String pkg = "package";
682 
683         // Post AUTOGROUP_AT_COUNT ongoing notifications
684         ArrayList<NotificationRecord> notifications = new ArrayList<>();
685         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
686             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
687                 UserHandle.SYSTEM);
688             notifications.add(r);
689         }
690 
691         for (NotificationRecord r: notifications) {
692             mGroupHelper.onNotificationPosted(r, false);
693         }
694 
695         // add ongoing
696         NotificationRecord r = getNotificationRecord(pkg, AUTOGROUP_AT_COUNT + 1, null,
697                 UserHandle.SYSTEM);
698         r.getNotification().flags |= FLAG_ONGOING_EVENT;
699         mGroupHelper.onNotificationPosted(r, true);
700 
701         // Summary is now ongoing
702         verify(mCallback).updateAutogroupSummary(anyInt(), anyString(), anyString(),
703                 eq(getNotificationAttributes(BASE_FLAGS | FLAG_ONGOING_EVENT)));
704     }
705 
706     @Test
testAutoGrouped_singleOngoing_appGroupOngoingChild()707     public void testAutoGrouped_singleOngoing_appGroupOngoingChild() {
708         final String pkg = "package";
709 
710         // Post AUTOGROUP_AT_COUNT ongoing notifications
711         ArrayList<NotificationRecord> notifications = new ArrayList<>();
712         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
713             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
714                 UserHandle.SYSTEM);
715             if (i == 0) {
716                 r.getNotification().flags |= FLAG_ONGOING_EVENT;
717             }
718             notifications.add(r);
719         }
720 
721         for (NotificationRecord r: notifications) {
722             mGroupHelper.onNotificationPosted(r, false);
723         }
724 
725         // app group the ongoing child
726         NotificationRecord r = getNotificationRecord(pkg, 0, "0", UserHandle.SYSTEM,
727                 "app group now", false);
728         mGroupHelper.onNotificationPosted(r, true);
729 
730         // Summary is no longer ongoing
731         verify(mCallback).updateAutogroupSummary(anyInt(), anyString(), anyString(),
732                 eq(getNotificationAttributes(BASE_FLAGS)));
733     }
734 
735     @Test
736     @DisableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
testAutoGrouped_singleOngoing_removeNonOngoingChild()737     public void testAutoGrouped_singleOngoing_removeNonOngoingChild() {
738         final String pkg = "package";
739 
740         // Post AUTOGROUP_AT_COUNT ongoing notifications
741         ArrayList<NotificationRecord> notifications = new ArrayList<>();
742         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
743             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
744                     UserHandle.SYSTEM);
745             if (i == 0) {
746                 r.getNotification().flags |= FLAG_ONGOING_EVENT;
747             }
748             notifications.add(r);
749         }
750 
751         for (NotificationRecord r: notifications) {
752             mGroupHelper.onNotificationPosted(r, false);
753         }
754 
755         // remove ongoing
756         mGroupHelper.onNotificationRemoved(notifications.get(1));
757 
758         // Summary is still ongoing
759         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
760                 any());
761     }
762 
763     @Test
764     @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
testAutoGrouped_singleOngoing_removeNonOngoingChild_forceGrouping()765     public void testAutoGrouped_singleOngoing_removeNonOngoingChild_forceGrouping() {
766         final String pkg = "package";
767 
768         // Post AUTOGROUP_AT_COUNT ongoing notifications
769         ArrayList<NotificationRecord> notifications = new ArrayList<>();
770         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
771             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
772                 UserHandle.SYSTEM);
773             if (i == 0) {
774                 r.getNotification().flags |= FLAG_ONGOING_EVENT;
775             }
776             notifications.add(r);
777         }
778 
779         for (NotificationRecord r: notifications) {
780             mGroupHelper.onNotificationPosted(r, false);
781         }
782 
783         // remove ongoing
784         mGroupHelper.onNotificationRemoved(notifications.get(1), new ArrayList<>(), false);
785 
786         // Summary is still ongoing
787         verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), anyString(), anyString(),
788                 any());
789     }
790 
791     @Test
testAutoGrouped_allAutoCancel_updateChildNotAutoCancel()792     public void testAutoGrouped_allAutoCancel_updateChildNotAutoCancel() {
793         final String pkg = "package";
794 
795         // Post AUTOGROUP_AT_COUNT ongoing notifications
796         ArrayList<NotificationRecord> notifications = new ArrayList<>();
797         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
798             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
799                     UserHandle.SYSTEM);
800             r.getNotification().flags |= FLAG_AUTO_CANCEL;
801             notifications.add(r);
802         }
803 
804         for (NotificationRecord r: notifications) {
805             mGroupHelper.onNotificationPosted(r, false);
806         }
807 
808         // One notification is no longer autocancelable
809         notifications.get(0).getNotification().flags &= ~FLAG_AUTO_CANCEL;
810         mGroupHelper.onNotificationPosted(notifications.get(0), true);
811 
812         // Summary should no longer be autocancelable
813         verify(mCallback).updateAutogroupSummary(anyInt(), anyString(), anyString(),
814                 eq(getNotificationAttributes(BASE_FLAGS)));
815     }
816 
817     @Test
testAutoGrouped_almostAllAutoCancel_updateChildAutoCancel()818     public void testAutoGrouped_almostAllAutoCancel_updateChildAutoCancel() {
819         final String pkg = "package";
820 
821         // Post AUTOGROUP_AT_COUNT ongoing notifications
822         ArrayList<NotificationRecord> notifications = new ArrayList<>();
823         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
824             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
825                     UserHandle.SYSTEM);
826             if (i != 0) {
827                 r.getNotification().flags |= FLAG_AUTO_CANCEL;
828             }
829             notifications.add(r);
830         }
831 
832         for (NotificationRecord r: notifications) {
833             mGroupHelper.onNotificationPosted(r, false);
834         }
835 
836         // Missing notification is now autocancelable
837         notifications.get(0).getNotification().flags |= FLAG_AUTO_CANCEL;
838         mGroupHelper.onNotificationPosted(notifications.get(0), true);
839 
840         // Summary should now autocancelable
841         verify(mCallback).updateAutogroupSummary(anyInt(), anyString(), anyString(),
842                 eq(getNotificationAttributes(BASE_FLAGS | FLAG_AUTO_CANCEL)));
843     }
844 
845     @Test
846     @DisableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
testAutoGrouped_allAutoCancel_updateChildAppGrouped()847     public void testAutoGrouped_allAutoCancel_updateChildAppGrouped() {
848         final String pkg = "package";
849 
850         // Post AUTOGROUP_AT_COUNT ongoing notifications
851         ArrayList<NotificationRecord> notifications = new ArrayList<>();
852         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
853             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
854                     UserHandle.SYSTEM);
855             r.getNotification().flags |= FLAG_AUTO_CANCEL;
856             notifications.add(r);
857         }
858 
859         for (NotificationRecord r: notifications) {
860             mGroupHelper.onNotificationPosted(r, false);
861         }
862 
863         // One notification is now grouped by app
864         NotificationRecord r = getNotificationRecord(pkg, 0, "0", UserHandle.SYSTEM,
865                 "app group now", false);
866         mGroupHelper.onNotificationPosted(r, true);
867 
868         // Summary should be still be autocancelable
869         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
870                 any());
871     }
872 
873     @Test
874     @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
testAutoGrouped_allAutoCancel_updateChildAppGrouped_forceGrouping()875     public void testAutoGrouped_allAutoCancel_updateChildAppGrouped_forceGrouping() {
876         final String pkg = "package";
877 
878         // Post AUTOGROUP_AT_COUNT ongoing notifications
879         ArrayList<NotificationRecord> notifications = new ArrayList<>();
880         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
881             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
882                 UserHandle.SYSTEM);
883             r.getNotification().flags |= FLAG_AUTO_CANCEL;
884             notifications.add(r);
885         }
886 
887         for (NotificationRecord r: notifications) {
888             mGroupHelper.onNotificationPosted(r, false);
889         }
890 
891         // One notification is now grouped by app
892         NotificationRecord r = getNotificationRecord(pkg, 0, "0", UserHandle.SYSTEM,
893             "app group now", false);
894         mGroupHelper.onNotificationPosted(r, true);
895 
896         // Summary should be still be autocancelable
897         verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), anyString(), anyString(),
898             any());
899     }
900 
901     @Test
902     @DisableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
testAutoGrouped_allAutoCancel_removeChild()903     public void testAutoGrouped_allAutoCancel_removeChild() {
904         final String pkg = "package";
905 
906         // Post AUTOGROUP_AT_COUNT ongoing notifications
907         ArrayList<NotificationRecord> notifications = new ArrayList<>();
908         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
909             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
910                     UserHandle.SYSTEM);
911             r.getNotification().flags |= FLAG_AUTO_CANCEL;
912             notifications.add(r);
913         }
914 
915         for (NotificationRecord r: notifications) {
916             mGroupHelper.onNotificationPosted(r, false);
917         }
918 
919         mGroupHelper.onNotificationRemoved(notifications.get(0));
920 
921         // Summary should still be autocancelable
922         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
923                 any());
924     }
925 
926     @Test
927     @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
testAutoGrouped_allAutoCancel_removeChild_forceGrouping()928     public void testAutoGrouped_allAutoCancel_removeChild_forceGrouping() {
929         final String pkg = "package";
930 
931         // Post AUTOGROUP_AT_COUNT ongoing notifications
932         ArrayList<NotificationRecord> notifications = new ArrayList<>();
933         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
934             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
935                 UserHandle.SYSTEM);
936             r.getNotification().flags |= FLAG_AUTO_CANCEL;
937             notifications.add(r);
938         }
939 
940         for (NotificationRecord r: notifications) {
941             mGroupHelper.onNotificationPosted(r, false);
942         }
943 
944         mGroupHelper.onNotificationRemoved(notifications.get(0), new ArrayList<>(), false);
945 
946         // Summary should still be autocancelable
947         verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), anyString(), anyString(),
948             any());
949     }
950 
951     @Test
952     @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
953     @DisableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
testDropToZeroRemoveGroup_disableFlag()954     public void testDropToZeroRemoveGroup_disableFlag() {
955         final String pkg = "package";
956         ArrayList<NotificationRecord> posted = new ArrayList<>();
957         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
958             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
959                     UserHandle.SYSTEM);
960             posted.add(r);
961             mGroupHelper.onNotificationPosted(r, false);
962         }
963         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(), anyString(),
964                 anyInt(), eq(getNotificationAttributes(BASE_FLAGS)));
965         verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(), anyString(),
966                 anyBoolean());
967         verify(mCallback, never()).removeAutoGroup(anyString());
968         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
969         Mockito.reset(mCallback);
970 
971         for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
972             mGroupHelper.onNotificationRemoved(posted.remove(0), new ArrayList<>(), false);
973         }
974         verify(mCallback, never()).removeAutoGroup(anyString());
975         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
976         Mockito.reset(mCallback);
977 
978         mGroupHelper.onNotificationRemoved(posted.remove(0), new ArrayList<>(), false);
979         verify(mCallback, never()).removeAutoGroup(anyString());
980         verify(mCallback, times(1)).removeAutoGroupSummary(anyInt(), anyString(), anyString());
981     }
982 
983     @Test
984     @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
985             android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST})
testDropToZeroRemoveGroup()986     public void testDropToZeroRemoveGroup() {
987         final String pkg = "package";
988         ArrayList<NotificationRecord> posted = new ArrayList<>();
989         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
990             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
991                     UserHandle.SYSTEM);
992             posted.add(r);
993             mGroupHelper.onNotificationPosted(r, false);
994         }
995         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(), anyString(),
996                 anyInt(), eq(getNotificationAttributes(BASE_FLAGS)));
997         verify(mCallback, times(AUTOGROUP_AT_COUNT - 1)).addAutoGroup(anyString(), anyString(),
998                 anyBoolean());
999         verify(mCallback, never()).removeAutoGroup(anyString());
1000         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
1001         Mockito.reset(mCallback);
1002 
1003         for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
1004             mGroupHelper.onNotificationRemoved(posted.remove(0), new ArrayList<>(), false);
1005         }
1006         verify(mCallback, never()).removeAutoGroup(anyString());
1007         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
1008         Mockito.reset(mCallback);
1009 
1010         mGroupHelper.onNotificationRemoved(posted.remove(0), new ArrayList<>(), false);
1011         verify(mCallback, never()).removeAutoGroup(anyString());
1012         verify(mCallback, times(1)).removeAutoGroupSummary(anyInt(), anyString(), anyString());
1013     }
1014 
1015     @Test
1016     @DisableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
testAppStartsGrouping_disableFlag()1017     public void testAppStartsGrouping_disableFlag() {
1018         final String pkg = "package";
1019         ArrayList<NotificationRecord> posted = new ArrayList<>();
1020         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
1021             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
1022                     UserHandle.SYSTEM);
1023             posted.add(r);
1024             mGroupHelper.onNotificationPosted(r, false);
1025         }
1026         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
1027                 anyString(), anyInt(), eq(getNotificationAttributes(BASE_FLAGS)));
1028         verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(), anyString(),
1029                 anyBoolean());
1030         verify(mCallback, never()).removeAutoGroup(anyString());
1031         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
1032         Mockito.reset(mCallback);
1033 
1034         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
1035             final NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
1036                     UserHandle.SYSTEM, "app group", false);
1037             r.getSbn().setOverrideGroupKey("autogrouped");
1038             mGroupHelper.onNotificationPosted(r, true);
1039             verify(mCallback, times(1)).removeAutoGroup(r.getKey());
1040             if (i < AUTOGROUP_AT_COUNT - 1) {
1041                 verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(),
1042                         anyString());
1043             }
1044         }
1045         verify(mCallback, times(1)).removeAutoGroupSummary(anyInt(), anyString(), anyString());
1046     }
1047 
1048     @Test
1049     @EnableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
testAppStartsGrouping()1050     public void testAppStartsGrouping() {
1051         final String pkg = "package";
1052         ArrayList<NotificationRecord> posted = new ArrayList<>();
1053         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
1054             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
1055                     UserHandle.SYSTEM);
1056             posted.add(r);
1057             mGroupHelper.onNotificationPosted(r, false);
1058         }
1059         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
1060                 anyString(), anyInt(), eq(getNotificationAttributes(BASE_FLAGS)));
1061         verify(mCallback, times(AUTOGROUP_AT_COUNT - 1)).addAutoGroup(anyString(), anyString(),
1062                 anyBoolean());
1063         verify(mCallback, never()).removeAutoGroup(anyString());
1064         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
1065         Mockito.reset(mCallback);
1066 
1067         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
1068             final NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
1069                     UserHandle.SYSTEM, "app group", false);
1070             r.getSbn().setOverrideGroupKey("autogrouped");
1071             mGroupHelper.onNotificationPosted(r, true);
1072             verify(mCallback, times(1)).removeAutoGroup(r.getKey());
1073             if (i < AUTOGROUP_AT_COUNT - 1) {
1074                 verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(),
1075                         anyString());
1076             }
1077         }
1078         verify(mCallback, times(1)).removeAutoGroupSummary(anyInt(), anyString(), anyString());
1079     }
1080 
1081     @Test
1082     @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
1083     @DisableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
testNewNotificationsAddedToAutogroup_ifOriginalNotificationsCanceled_alwaysGroup()1084     public void testNewNotificationsAddedToAutogroup_ifOriginalNotificationsCanceled_alwaysGroup() {
1085         final String pkg = "package";
1086         ArrayList<NotificationRecord> posted = new ArrayList<>();
1087         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
1088             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
1089                 UserHandle.SYSTEM);
1090             posted.add(r);
1091             mGroupHelper.onNotificationPosted(r, false);
1092         }
1093         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
1094                 anyString(), anyInt(), eq(getNotificationAttributes(BASE_FLAGS)));
1095         verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(), anyString(),
1096                 anyBoolean());
1097         verify(mCallback, never()).removeAutoGroup(anyString());
1098         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
1099         Mockito.reset(mCallback);
1100 
1101         for (int i = posted.size() - 2; i >= 0; i--) {
1102             mGroupHelper.onNotificationRemoved(posted.remove(i), new ArrayList<>(), false);
1103         }
1104         verify(mCallback, never()).removeAutoGroup(anyString());
1105         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
1106         Mockito.reset(mCallback);
1107 
1108         // Add new notification; it should be autogrouped even though the total count is
1109         // < AUTOGROUP_AT_COUNT
1110         final NotificationRecord r = getNotificationRecord(pkg, 5, String.valueOf(5),
1111                 UserHandle.SYSTEM);
1112         final String autogroupKey = getExpectedAutogroupKey(r);
1113         posted.add(r);
1114         assertThat(mGroupHelper.onNotificationPosted(r, true)).isFalse();
1115         verify(mCallback, times(1)).addAutoGroup(r.getKey(), autogroupKey, true);
1116         verify(mCallback, never()).removeAutoGroup(anyString());
1117         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
1118         verify(mCallback).updateAutogroupSummary(anyInt(), anyString(), anyString(),
1119                 eq(getNotificationAttributes(BASE_FLAGS)));
1120         verify(mCallback, never()).addAutoGroupSummary(anyInt(), anyString(), anyString(),
1121                 anyString(), anyInt(), any());
1122     }
1123 
1124     @Test
1125     @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
1126             android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST})
testNewNotificationsAddedToAutogroup_ifOriginalNotificationsCanceled()1127     public void testNewNotificationsAddedToAutogroup_ifOriginalNotificationsCanceled() {
1128         final String pkg = "package";
1129         ArrayList<NotificationRecord> posted = new ArrayList<>();
1130         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
1131             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
1132                 UserHandle.SYSTEM);
1133             posted.add(r);
1134             mGroupHelper.onNotificationPosted(r, false);
1135         }
1136 
1137         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
1138                 anyString(), anyInt(), eq(getNotificationAttributes(BASE_FLAGS)));
1139         verify(mCallback, times(AUTOGROUP_AT_COUNT - 1)).addAutoGroup(anyString(), anyString(),
1140                 anyBoolean());
1141         verify(mCallback, never()).removeAutoGroup(anyString());
1142         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
1143         Mockito.reset(mCallback);
1144 
1145         for (int i = posted.size() - 2; i >= 0; i--) {
1146             mGroupHelper.onNotificationRemoved(posted.remove(i), new ArrayList<>(), false);
1147         }
1148         verify(mCallback, never()).removeAutoGroup(anyString());
1149         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
1150         Mockito.reset(mCallback);
1151 
1152         // Add new notification; it should be autogrouped even though the total count is
1153         // < AUTOGROUP_AT_COUNT
1154         final NotificationRecord r = getNotificationRecord(pkg, 5, String.valueOf(5),
1155                 UserHandle.SYSTEM);
1156         posted.add(r);
1157         assertThat(mGroupHelper.onNotificationPosted(r, true)).isTrue();
1158         // addAutoGroup not called on sbn, because the autogrouping is expected to be done
1159         // synchronously.
1160         verify(mCallback, never()).removeAutoGroup(anyString());
1161         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
1162         verify(mCallback).updateAutogroupSummary(anyInt(), anyString(), anyString(),
1163                 eq(getNotificationAttributes(BASE_FLAGS)));
1164         verify(mCallback, never()).addAutoGroupSummary(anyInt(), anyString(), anyString(),
1165                 anyString(), anyInt(), any());
1166     }
1167 
1168     @Test
1169     @DisableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
1170     @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE)
testAddSummary_sameIcon_sameColor_alwaysAutogroup()1171     public void testAddSummary_sameIcon_sameColor_alwaysAutogroup() {
1172         final String pkg = "package";
1173         final Icon icon = mock(Icon.class);
1174         when(icon.sameAs(icon)).thenReturn(true);
1175         final int iconColor = Color.BLUE;
1176         final NotificationAttributes attr = new NotificationAttributes(BASE_FLAGS, icon, iconColor,
1177                 DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT, TEST_CHANNEL_ID);
1178 
1179         // Add notifications with same icon and color
1180         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
1181             NotificationRecord r = getNotificationRecord(
1182                     getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM, null, icon, iconColor));
1183             mGroupHelper.onNotificationPosted(r, false);
1184         }
1185         // Check that the summary would have the same icon and color
1186         verify(mCallback, times(1)).addAutoGroupSummary(
1187                 anyInt(), eq(pkg), anyString(), anyString(), anyInt(), eq(attr));
1188         verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(), anyString(),
1189                 anyBoolean());
1190         verify(mCallback, never()).removeAutoGroup(anyString());
1191         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
1192 
1193         // After auto-grouping, add new notification with the same color
1194         NotificationRecord r = getNotificationRecord(
1195                 getSbn(pkg, AUTOGROUP_AT_COUNT, String.valueOf(AUTOGROUP_AT_COUNT),
1196                     UserHandle.SYSTEM,null, icon, iconColor));
1197         mGroupHelper.onNotificationPosted(r, true);
1198 
1199         // Check that the summary was updated
1200         //NotificationAttributes newAttr = new NotificationAttributes(BASE_FLAGS, icon, iconColor);
1201         verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), anyString(), anyString(),
1202                 eq(attr));
1203     }
1204 
1205     @Test
1206     @EnableFlags({Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE,
1207             android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST})
testAddSummary_sameIcon_sameColor()1208     public void testAddSummary_sameIcon_sameColor() {
1209         final String pkg = "package";
1210         final Icon icon = mock(Icon.class);
1211         when(icon.sameAs(icon)).thenReturn(true);
1212         final int iconColor = Color.BLUE;
1213         final NotificationAttributes attr = new NotificationAttributes(BASE_FLAGS, icon, iconColor,
1214                 DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT, TEST_CHANNEL_ID);
1215 
1216         // Add notifications with same icon and color
1217         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
1218             NotificationRecord r = getNotificationRecord(
1219                     getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM, null, icon, iconColor));
1220             mGroupHelper.onNotificationPosted(r, false);
1221         }
1222         // Check that the summary would have the same icon and color
1223         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
1224                 anyString(), anyInt(), eq(attr));
1225         verify(mCallback, times(AUTOGROUP_AT_COUNT - 1)).addAutoGroup(anyString(), anyString(),
1226                 anyBoolean());
1227         verify(mCallback, never()).removeAutoGroup(anyString());
1228         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
1229 
1230         // After auto-grouping, add new notification with the same color
1231         NotificationRecord r = getNotificationRecord(getSbn(pkg, AUTOGROUP_AT_COUNT,
1232             String.valueOf(AUTOGROUP_AT_COUNT), UserHandle.SYSTEM, null, icon, iconColor));
1233         mGroupHelper.onNotificationPosted(r, true);
1234 
1235         // Check that the summary was updated
1236         //NotificationAttributes newAttr = new NotificationAttributes(BASE_FLAGS, icon, iconColor);
1237         verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), anyString(), anyString(),
1238                 eq(attr));
1239     }
1240 
1241     @Test
1242     @DisableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
1243     @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE)
testAddSummary_diffIcon_diffColor_disableFlag()1244     public void testAddSummary_diffIcon_diffColor_disableFlag() {
1245         final String pkg = "package";
1246         final Icon initialIcon = mock(Icon.class);
1247         when(initialIcon.sameAs(initialIcon)).thenReturn(true);
1248         final int initialIconColor = Color.BLUE;
1249 
1250         // Spy GroupHelper for getMonochromeAppIcon
1251         final Icon monochromeIcon = mock(Icon.class);
1252         when(monochromeIcon.sameAs(monochromeIcon)).thenReturn(true);
1253         GroupHelper groupHelper = spy(mGroupHelper);
1254         doReturn(monochromeIcon).when(groupHelper).getMonochromeAppIcon(eq(pkg));
1255 
1256         final NotificationAttributes initialAttr = new NotificationAttributes(BASE_FLAGS,
1257                 initialIcon, initialIconColor, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT,
1258                 TEST_CHANNEL_ID);
1259 
1260         // Add notifications with same icon and color
1261         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
1262             NotificationRecord r = getNotificationRecord(
1263                 getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM, null,
1264                     initialIcon, initialIconColor));
1265             groupHelper.onNotificationPosted(r, false);
1266         }
1267         // Check that the summary would have the same icon and color
1268         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
1269                 anyString(), anyInt(), eq(initialAttr));
1270         verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(), anyString(),
1271                 anyBoolean());
1272         verify(mCallback, never()).removeAutoGroup(anyString());
1273         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
1274 
1275         // After auto-grouping, add new notification with a different color
1276         final Icon newIcon = mock(Icon.class);
1277         final int newIconColor = Color.YELLOW;
1278         NotificationRecord r = getNotificationRecord(getSbn(pkg, AUTOGROUP_AT_COUNT,
1279                 String.valueOf(AUTOGROUP_AT_COUNT), UserHandle.SYSTEM, null, newIcon,
1280                 newIconColor));
1281         groupHelper.onNotificationPosted(r, true);
1282 
1283         // Summary should be updated to the default color and the icon to the monochrome icon
1284         NotificationAttributes newAttr = new NotificationAttributes(BASE_FLAGS, monochromeIcon,
1285                 COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT, TEST_CHANNEL_ID);
1286         verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), anyString(), anyString(),
1287                 eq(newAttr));
1288     }
1289 
1290     @Test
1291     @EnableFlags({Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE,
1292             android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST})
testAddSummary_diffIcon_diffColor()1293     public void testAddSummary_diffIcon_diffColor() {
1294         final String pkg = "package";
1295         final Icon initialIcon = mock(Icon.class);
1296         when(initialIcon.sameAs(initialIcon)).thenReturn(true);
1297         final int initialIconColor = Color.BLUE;
1298 
1299         // Spy GroupHelper for getMonochromeAppIcon
1300         final Icon monochromeIcon = mock(Icon.class);
1301         when(monochromeIcon.sameAs(monochromeIcon)).thenReturn(true);
1302         GroupHelper groupHelper = spy(mGroupHelper);
1303         doReturn(monochromeIcon).when(groupHelper).getMonochromeAppIcon(eq(pkg));
1304 
1305         final NotificationAttributes initialAttr = new NotificationAttributes(BASE_FLAGS,
1306                 initialIcon, initialIconColor, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT,
1307                 TEST_CHANNEL_ID);
1308 
1309         // Add notifications with same icon and color
1310         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
1311             NotificationRecord r = getNotificationRecord(
1312                 getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM, null,
1313                     initialIcon, initialIconColor));
1314             groupHelper.onNotificationPosted(r, false);
1315         }
1316         // Check that the summary would have the same icon and color
1317         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
1318                 anyString(), anyInt(), eq(initialAttr));
1319         verify(mCallback, times(AUTOGROUP_AT_COUNT - 1)).addAutoGroup(anyString(), anyString(),
1320                 anyBoolean());
1321         verify(mCallback, never()).removeAutoGroup(anyString());
1322         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
1323 
1324         // After auto-grouping, add new notification with a different color
1325         final Icon newIcon = mock(Icon.class);
1326         final int newIconColor = Color.YELLOW;
1327         NotificationRecord r = getNotificationRecord(getSbn(pkg, AUTOGROUP_AT_COUNT,
1328             String.valueOf(AUTOGROUP_AT_COUNT), UserHandle.SYSTEM, null, newIcon,
1329             newIconColor));
1330         groupHelper.onNotificationPosted(r, true);
1331 
1332         // Summary should be updated to the default color and the icon to the monochrome icon
1333         NotificationAttributes newAttr = new NotificationAttributes(BASE_FLAGS, monochromeIcon,
1334                 COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT, TEST_CHANNEL_ID);
1335         verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), anyString(), anyString(),
1336                 eq(newAttr));
1337     }
1338 
1339     @Test
1340     @DisableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
1341     @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE)
testAddSummary_diffVisibility_alwaysAutogroup()1342     public void testAddSummary_diffVisibility_alwaysAutogroup() {
1343         final String pkg = "package";
1344         final Icon icon = mock(Icon.class);
1345         when(icon.sameAs(icon)).thenReturn(true);
1346         final int iconColor = Color.BLUE;
1347         final NotificationAttributes attr = new NotificationAttributes(BASE_FLAGS, icon, iconColor,
1348                 VISIBILITY_PRIVATE, DEFAULT_GROUP_ALERT, TEST_CHANNEL_ID);
1349 
1350         // Add notifications with same icon and color and default visibility (private)
1351         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
1352             NotificationRecord r = getNotificationRecord(
1353                 getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM, null,
1354                     icon, iconColor));
1355             mGroupHelper.onNotificationPosted(r, false);
1356         }
1357         // Check that the summary has private visibility
1358         verify(mCallback, times(1)).addAutoGroupSummary(
1359                 anyInt(), eq(pkg), anyString(), anyString(), anyInt(), eq(attr));
1360 
1361         verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(), anyString(),
1362                 anyBoolean());
1363         verify(mCallback, never()).removeAutoGroup(anyString());
1364         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
1365 
1366         // After auto-grouping, add new notification with public visibility
1367         NotificationRecord r = getNotificationRecord(getSbn(pkg, AUTOGROUP_AT_COUNT,
1368             String.valueOf(AUTOGROUP_AT_COUNT), UserHandle.SYSTEM, null, icon, iconColor));
1369         r.getNotification().visibility = VISIBILITY_PUBLIC;
1370         mGroupHelper.onNotificationPosted(r, true);
1371 
1372         // Check that the summary visibility was updated
1373         NotificationAttributes newAttr = new NotificationAttributes(BASE_FLAGS, icon, iconColor,
1374                 VISIBILITY_PUBLIC, DEFAULT_GROUP_ALERT, TEST_CHANNEL_ID);
1375         verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), anyString(), anyString(),
1376                 eq(newAttr));
1377     }
1378 
1379     @Test
1380     @EnableFlags({Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE,
1381             android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST})
testAddSummary_diffVisibility()1382     public void testAddSummary_diffVisibility() {
1383         final String pkg = "package";
1384         final Icon icon = mock(Icon.class);
1385         when(icon.sameAs(icon)).thenReturn(true);
1386         final int iconColor = Color.BLUE;
1387         final NotificationAttributes attr = new NotificationAttributes(BASE_FLAGS, icon, iconColor,
1388                 VISIBILITY_PRIVATE, DEFAULT_GROUP_ALERT, TEST_CHANNEL_ID);
1389 
1390         // Add notifications with same icon and color and default visibility (private)
1391         for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
1392             NotificationRecord r = getNotificationRecord(
1393                 getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM, null,
1394                     icon, iconColor));
1395             assertThat(mGroupHelper.onNotificationPosted(r, false)).isFalse();
1396         }
1397         // The last notification added will reach the autogroup threshold.
1398         NotificationRecord r = getNotificationRecord(getSbn(pkg, AUTOGROUP_AT_COUNT - 1,
1399             String.valueOf(AUTOGROUP_AT_COUNT - 1), UserHandle.SYSTEM, null, icon, iconColor));
1400         assertThat(mGroupHelper.onNotificationPosted(r, false)).isTrue();
1401 
1402         // Check that the summary has private visibility
1403         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(), anyString(),
1404                 anyInt(), eq(attr));
1405         // The last sbn is expected to be added to autogroup synchronously.
1406         verify(mCallback, times(AUTOGROUP_AT_COUNT - 1)).addAutoGroup(anyString(), anyString(),
1407                 anyBoolean());
1408         verify(mCallback, never()).removeAutoGroup(anyString());
1409         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
1410 
1411         // After auto-grouping, add new notification with public visibility
1412         r = getNotificationRecord(getSbn(pkg, AUTOGROUP_AT_COUNT,
1413             String.valueOf(AUTOGROUP_AT_COUNT), UserHandle.SYSTEM, null, icon, iconColor));
1414         r.getNotification().visibility = VISIBILITY_PUBLIC;
1415         assertThat(mGroupHelper.onNotificationPosted(r, true)).isTrue();
1416 
1417         // Check that the summary visibility was updated
1418         NotificationAttributes newAttr = new NotificationAttributes(BASE_FLAGS, icon, iconColor,
1419                 VISIBILITY_PUBLIC, DEFAULT_GROUP_ALERT, TEST_CHANNEL_ID);
1420         verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), anyString(), anyString(),
1421                 eq(newAttr));
1422     }
1423 
1424     @Test
1425     @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE)
1426     @DisableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
testAutoGrouped_diffIcon_diffColor_removeChild_updateTo_sameIcon_sameColor()1427     public void testAutoGrouped_diffIcon_diffColor_removeChild_updateTo_sameIcon_sameColor() {
1428         final String pkg = "package";
1429         final Icon initialIcon = mock(Icon.class);
1430         when(initialIcon.sameAs(initialIcon)).thenReturn(true);
1431         final int initialIconColor = Color.BLUE;
1432         final NotificationAttributes initialAttr = new NotificationAttributes(
1433                 GroupHelper.FLAG_INVALID, initialIcon, initialIconColor, DEFAULT_VISIBILITY,
1434                 DEFAULT_GROUP_ALERT, TEST_CHANNEL_ID);
1435 
1436         // Add AUTOGROUP_AT_COUNT-1 notifications with same icon and color
1437         ArrayList<NotificationRecord> notifications = new ArrayList<>();
1438         for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
1439             NotificationRecord r = getNotificationRecord(
1440                 getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM, null,
1441                     initialIcon, initialIconColor));
1442             notifications.add(r);
1443         }
1444         // And an additional notification with different icon and color
1445         final int lastIdx = AUTOGROUP_AT_COUNT - 1;
1446         NotificationRecord newRec = getNotificationRecord(getSbn(pkg, lastIdx,
1447                 String.valueOf(lastIdx), UserHandle.SYSTEM, null, mock(Icon.class),
1448                 Color.YELLOW));
1449         notifications.add(newRec);
1450         for (NotificationRecord r: notifications) {
1451             mGroupHelper.onNotificationPosted(r, false);
1452         }
1453 
1454         // Remove last notification (the only one with different icon and color)
1455         mGroupHelper.onNotificationRemoved(notifications.get(lastIdx));
1456 
1457         // Summary should be updated to the common icon and color
1458         verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), anyString(), anyString(),
1459                 eq(initialAttr));
1460     }
1461 
1462     @Test
1463     @EnableFlags({Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE,
1464             FLAG_NOTIFICATION_FORCE_GROUPING})
testAutoGrouped_diffIcon_diffColor_removeChild_updateTo_sameIcon_sameColor_forceGrouping()1465     public void testAutoGrouped_diffIcon_diffColor_removeChild_updateTo_sameIcon_sameColor_forceGrouping() {
1466         final String pkg = "package";
1467         final Icon initialIcon = mock(Icon.class);
1468         when(initialIcon.sameAs(initialIcon)).thenReturn(true);
1469         final int initialIconColor = Color.BLUE;
1470         final NotificationAttributes initialAttr = new NotificationAttributes(
1471             BASE_FLAGS, initialIcon, initialIconColor, DEFAULT_VISIBILITY,
1472             DEFAULT_GROUP_ALERT, TEST_CHANNEL_ID);
1473 
1474         // Add AUTOGROUP_AT_COUNT-1 notifications with same icon and color
1475         ArrayList<NotificationRecord> notifications = new ArrayList<>();
1476         for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
1477             NotificationRecord r = getNotificationRecord(
1478                 getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM, null,
1479                     initialIcon, initialIconColor));
1480             notifications.add(r);
1481         }
1482         // And an additional notification with different icon and color
1483         final int lastIdx = AUTOGROUP_AT_COUNT - 1;
1484         NotificationRecord newRec = getNotificationRecord(getSbn(pkg, lastIdx,
1485             String.valueOf(lastIdx), UserHandle.SYSTEM, null, mock(Icon.class),
1486             Color.YELLOW));
1487         notifications.add(newRec);
1488         for (NotificationRecord r: notifications) {
1489             mGroupHelper.onNotificationPosted(r, false);
1490         }
1491 
1492         // Remove last notification (the only one with different icon and color)
1493         mGroupHelper.onNotificationRemoved(notifications.get(lastIdx), new ArrayList<>(), false);
1494 
1495         // Summary should be updated to the common icon and color
1496         verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), anyString(), anyString(),
1497             eq(initialAttr));
1498     }
1499 
1500     @Test
1501     @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE)
testAutobundledSummaryIcon_sameIcon()1502     public void testAutobundledSummaryIcon_sameIcon() {
1503         final String pkg = "package";
1504         final Icon icon = mock(Icon.class);
1505         when(icon.sameAs(icon)).thenReturn(true);
1506 
1507         // Create notifications with the same icon
1508         List<NotificationAttributes> childrenAttr = new ArrayList<>();
1509         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
1510             childrenAttr.add(new NotificationAttributes(0, icon, COLOR_DEFAULT,
1511                     DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT, TEST_CHANNEL_ID));
1512         }
1513 
1514         //Check that the generated summary icon is the same as the child notifications'
1515         Icon summaryIcon = mGroupHelper.getAutobundledSummaryAttributes(pkg, childrenAttr).icon;
1516         assertThat(summaryIcon).isEqualTo(icon);
1517     }
1518 
1519     @Test
1520     @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE)
testAutobundledSummaryIcon_diffIcon()1521     public void testAutobundledSummaryIcon_diffIcon() {
1522         final String pkg = "package";
1523         // Spy GroupHelper for getMonochromeAppIcon
1524         final Icon monochromeIcon = mock(Icon.class);
1525         GroupHelper groupHelper = spy(mGroupHelper);
1526         doReturn(monochromeIcon).when(groupHelper).getMonochromeAppIcon(eq(pkg));
1527 
1528         // Create notifications with different icons
1529         List<NotificationAttributes> childrenAttr = new ArrayList<>();
1530         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
1531             childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), COLOR_DEFAULT,
1532                     DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT, TEST_CHANNEL_ID));
1533         }
1534 
1535         // Check that the generated summary icon is the monochrome icon
1536         Icon summaryIcon = groupHelper.getAutobundledSummaryAttributes(pkg, childrenAttr).icon;
1537         assertThat(summaryIcon).isEqualTo(monochromeIcon);
1538     }
1539 
1540     @Test
1541     @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE)
testAutobundledSummaryIconColor_sameColor()1542     public void testAutobundledSummaryIconColor_sameColor() {
1543         final String pkg = "package";
1544         final int iconColor = Color.BLUE;
1545         // Create notifications with the same icon color
1546         List<NotificationAttributes> childrenAttr = new ArrayList<>();
1547         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
1548             childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), iconColor,
1549                     DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT, TEST_CHANNEL_ID));
1550         }
1551 
1552         // Check that the generated summary icon color is the same as the child notifications'
1553         int summaryIconColor = mGroupHelper.getAutobundledSummaryAttributes(pkg,
1554                 childrenAttr).iconColor;
1555         assertThat(summaryIconColor).isEqualTo(iconColor);
1556     }
1557 
1558     @Test
1559     @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE)
testAutobundledSummaryIconColor_diffColor()1560     public void testAutobundledSummaryIconColor_diffColor() {
1561         final String pkg = "package";
1562         // Create notifications with different icon colors
1563         List<NotificationAttributes> childrenAttr = new ArrayList<>();
1564         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
1565             childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), i,
1566                     DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT, TEST_CHANNEL_ID));
1567         }
1568 
1569         // Check that the generated summary icon color is the default color
1570         int summaryIconColor = mGroupHelper.getAutobundledSummaryAttributes(pkg,
1571                 childrenAttr).iconColor;
1572         assertThat(summaryIconColor).isEqualTo(Notification.COLOR_DEFAULT);
1573     }
1574 
1575     @Test
1576     @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE)
testAutobundledSummaryVisibility_hasPublicChildren()1577     public void testAutobundledSummaryVisibility_hasPublicChildren() {
1578         final String pkg = "package";
1579         final int iconColor = Color.BLUE;
1580         // Create notifications with private and public visibility
1581         List<NotificationAttributes> childrenAttr = new ArrayList<>();
1582         childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), iconColor,
1583                 VISIBILITY_PUBLIC, DEFAULT_GROUP_ALERT, TEST_CHANNEL_ID));
1584         for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
1585             childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), iconColor,
1586                     VISIBILITY_PRIVATE, DEFAULT_GROUP_ALERT, TEST_CHANNEL_ID));
1587         }
1588 
1589         // Check that the generated summary visibility is public
1590         int summaryVisibility = mGroupHelper.getAutobundledSummaryAttributes(pkg,
1591                 childrenAttr).visibility;
1592         assertThat(summaryVisibility).isEqualTo(VISIBILITY_PUBLIC);
1593     }
1594 
1595     @Test
1596     @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE)
testAutobundledSummaryVisibility_noPublicChildren()1597     public void testAutobundledSummaryVisibility_noPublicChildren() {
1598         final String pkg = "package";
1599         final int iconColor = Color.BLUE;
1600         int visibility = VISIBILITY_PRIVATE;
1601         // Create notifications with either private or secret visibility
1602         List<NotificationAttributes> childrenAttr = new ArrayList<>();
1603         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
1604             if (i % 2 == 0) {
1605                 visibility = VISIBILITY_PRIVATE;
1606             } else {
1607                 visibility = VISIBILITY_SECRET;
1608             }
1609             childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), iconColor,
1610                     visibility, DEFAULT_GROUP_ALERT, TEST_CHANNEL_ID));
1611         }
1612 
1613         // Check that the generated summary visibility is private
1614         int summaryVisibility = mGroupHelper.getAutobundledSummaryAttributes(pkg,
1615                 childrenAttr).visibility;
1616         assertThat(summaryVisibility).isEqualTo(VISIBILITY_PRIVATE);
1617     }
1618 
1619     @Test
testAutobundledSummaryAlertBehavior_oneChildAlertChildren()1620     public void testAutobundledSummaryAlertBehavior_oneChildAlertChildren() {
1621         final String pkg = "package";
1622         final int iconColor = Color.BLUE;
1623         // Create notifications with GROUP_ALERT_SUMMARY + one with GROUP_ALERT_CHILDREN
1624         List<NotificationAttributes> childrenAttr = new ArrayList<>();
1625         childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), iconColor,
1626                 VISIBILITY_PUBLIC, GROUP_ALERT_CHILDREN, TEST_CHANNEL_ID));
1627         for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
1628             childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), iconColor,
1629                 VISIBILITY_PRIVATE, GROUP_ALERT_SUMMARY, TEST_CHANNEL_ID));
1630         }
1631         // Check that the generated summary alert behavior is GROUP_ALERT_CHILDREN
1632         int groupAlertBehavior = mGroupHelper.getAutobundledSummaryAttributes(pkg,
1633                 childrenAttr).groupAlertBehavior;
1634         assertThat(groupAlertBehavior).isEqualTo(GROUP_ALERT_CHILDREN);
1635     }
1636 
1637     @Test
testAutobundledSummaryAlertBehavior_oneChildAlertAll()1638     public void testAutobundledSummaryAlertBehavior_oneChildAlertAll() {
1639         final String pkg = "package";
1640         final int iconColor = Color.BLUE;
1641         // Create notifications with GROUP_ALERT_SUMMARY + one with GROUP_ALERT_ALL
1642         List<NotificationAttributes> childrenAttr = new ArrayList<>();
1643         childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), iconColor,
1644                 VISIBILITY_PUBLIC, GROUP_ALERT_ALL, TEST_CHANNEL_ID));
1645         for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
1646             childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), iconColor,
1647                 VISIBILITY_PRIVATE, GROUP_ALERT_SUMMARY, TEST_CHANNEL_ID));
1648         }
1649         // Check that the generated summary alert behavior is GROUP_ALERT_CHILDREN
1650         int groupAlertBehavior = mGroupHelper.getAutobundledSummaryAttributes(pkg,
1651                 childrenAttr).groupAlertBehavior;
1652         assertThat(groupAlertBehavior).isEqualTo(GROUP_ALERT_CHILDREN);
1653     }
1654 
1655     @Test
testAutobundledSummaryAlertBehavior_allChildAlertSummary()1656     public void testAutobundledSummaryAlertBehavior_allChildAlertSummary() {
1657         final String pkg = "package";
1658         final int iconColor = Color.BLUE;
1659         // Create notifications with GROUP_ALERT_SUMMARY
1660         List<NotificationAttributes> childrenAttr = new ArrayList<>();
1661         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
1662             childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), iconColor,
1663                 VISIBILITY_PRIVATE, GROUP_ALERT_SUMMARY, TEST_CHANNEL_ID));
1664         }
1665 
1666         // Check that the generated summary alert behavior is GROUP_ALERT_SUMMARY
1667         int groupAlertBehavior = mGroupHelper.getAutobundledSummaryAttributes(pkg,
1668                 childrenAttr).groupAlertBehavior;
1669         assertThat(groupAlertBehavior).isEqualTo(GROUP_ALERT_SUMMARY);
1670     }
1671 
1672     @Test
1673     @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE)
testAutobundledSummaryChannelId()1674     public void testAutobundledSummaryChannelId() {
1675         final String pkg = "package";
1676         final int iconColor = Color.BLUE;
1677         final String expectedChannelId = TEST_CHANNEL_ID + "0";
1678         // Create notifications with different channelIds
1679         List<NotificationAttributes> childrenAttr = new ArrayList<>();
1680         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
1681             childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), iconColor,
1682                     DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT, TEST_CHANNEL_ID+i));
1683         }
1684 
1685         // Check that the generated summary channelId is the first child in the list
1686         String summaryChannelId = mGroupHelper.getAutobundledSummaryAttributes(pkg,
1687                 childrenAttr).channelId;
1688         assertThat(summaryChannelId).isEqualTo(expectedChannelId);
1689     }
1690 
1691     @Test
1692     @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE)
testAutobundledSummaryChannelId_noChildren()1693     public void testAutobundledSummaryChannelId_noChildren() {
1694         final String pkg = "package";
1695         // No child notifications
1696         List<NotificationAttributes> childrenAttr = new ArrayList<>();
1697         // Check that the generated summary channelId is null
1698         String summaryChannelId = mGroupHelper.getAutobundledSummaryAttributes(pkg,
1699                 childrenAttr).channelId;
1700         assertThat(summaryChannelId).isNull();
1701     }
1702 
1703     @Test
1704     @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE)
testMonochromeAppIcon_adaptiveIconExists()1705     public void testMonochromeAppIcon_adaptiveIconExists() throws Exception {
1706         final String pkg = "testPackage";
1707         final int monochromeIconResId = 1234;
1708         AdaptiveIconDrawable adaptiveIcon = mock(AdaptiveIconDrawable.class);
1709         Drawable monochromeIcon = mock(Drawable.class);
1710         when(mPackageManager.getApplicationIcon(pkg)).thenReturn(adaptiveIcon);
1711         when(adaptiveIcon.getMonochrome()).thenReturn(monochromeIcon);
1712         when(adaptiveIcon.getSourceDrawableResId()).thenReturn(monochromeIconResId);
1713         assertThat(mGroupHelper.getMonochromeAppIcon(pkg).getResId())
1714                 .isEqualTo(monochromeIconResId);
1715     }
1716 
1717     @Test
1718     @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE)
testMonochromeAppIcon_adaptiveIconMissing_fallback()1719     public void testMonochromeAppIcon_adaptiveIconMissing_fallback() throws Exception {
1720         final String pkg = "testPackage";
1721         final int fallbackIconResId = R.drawable.ic_notification_summary_auto;
1722         when(mPackageManager.getApplicationIcon(pkg)).thenReturn(mock(Drawable.class));
1723         assertThat(mGroupHelper.getMonochromeAppIcon(pkg).getResId())
1724                 .isEqualTo(fallbackIconResId);
1725     }
1726 
1727     @Test
1728     @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
testGetAggregateGroupKey()1729     public void testGetAggregateGroupKey() {
1730         final String fullAggregateGroupKey = GroupHelper.getFullAggregateGroupKey("pkg",
1731                 "groupKey", 1234);
1732         assertThat(fullAggregateGroupKey).isEqualTo("1234|pkg|g:groupKey");
1733     }
1734 
1735     @Test
1736     @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
testNoGroup_postingUnderLimit_forcedGrouping()1737     public void testNoGroup_postingUnderLimit_forcedGrouping() {
1738         final List<NotificationRecord> notificationList = new ArrayList<>();
1739         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
1740         final String pkg = "package";
1741         for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
1742             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
1743                     UserHandle.SYSTEM, "testGrp " + i, true);
1744             notificationList.add(r);
1745             mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
1746         }
1747         verifyNoMoreInteractions(mCallback);
1748     }
1749 
1750     @Test
1751     @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
testNoGroup_AutobundledAlready_forcedGrouping()1752     public void testNoGroup_AutobundledAlready_forcedGrouping() {
1753         final List<NotificationRecord> notificationList = new ArrayList<>();
1754         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
1755         final String pkg = "package";
1756         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
1757             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
1758                     UserHandle.SYSTEM, null, true);
1759             notificationList.add(r);
1760             mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
1761         }
1762         verifyNoMoreInteractions(mCallback);
1763     }
1764 
1765     @Test
1766     @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
testNoGroup_isCanceled_forcedGrouping()1767     public void testNoGroup_isCanceled_forcedGrouping() {
1768         final List<NotificationRecord> notificationList = new ArrayList<>();
1769         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
1770         final String pkg = "package";
1771         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
1772             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
1773                     UserHandle.SYSTEM, "testGrp" + i, true);
1774             r.isCanceled = true;
1775             notificationList.add(r);
1776             mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
1777         }
1778         verifyNoMoreInteractions(mCallback);
1779     }
1780 
1781     @Test
1782     @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
testNoGroup_isAggregated_forcedGrouping()1783     public void testNoGroup_isAggregated_forcedGrouping() {
1784         final List<NotificationRecord> notificationList = new ArrayList<>();
1785         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
1786         final String pkg = "package";
1787         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
1788             String aggregateGroupKey = AGGREGATE_GROUP_KEY + "AlertingSection";
1789             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
1790                     UserHandle.SYSTEM, aggregateGroupKey, true);
1791             notificationList.add(r);
1792             mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
1793         }
1794         verifyNoMoreInteractions(mCallback);
1795     }
1796 
1797     @Test
1798     @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
testNoGroup_multiPackage_forcedGrouping()1799     public void testNoGroup_multiPackage_forcedGrouping() {
1800         final String pkg = "package";
1801         final String pkg2 = "package2";
1802         final List<NotificationRecord> notificationList = new ArrayList<>();
1803         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
1804         for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
1805             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
1806                     UserHandle.SYSTEM, "testGrp " + i, true);
1807             notificationList.add(r);
1808             mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
1809         }
1810         NotificationRecord r = getNotificationRecord(pkg2, AUTOGROUP_AT_COUNT,
1811                 String.valueOf(AUTOGROUP_AT_COUNT), UserHandle.SYSTEM, "testGrp", true);
1812         notificationList.add(r);
1813         mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
1814         verifyNoMoreInteractions(mCallback);
1815     }
1816 
1817     @Test
1818     @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
testNoGroup_multiUser_forcedGrouping()1819     public void testNoGroup_multiUser_forcedGrouping() {
1820         final String pkg = "package";
1821         final List<NotificationRecord> notificationList = new ArrayList<>();
1822         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
1823         for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
1824             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
1825                     UserHandle.SYSTEM, "testGrp " + i, true);
1826             notificationList.add(r);
1827             mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
1828         }
1829         NotificationRecord r = getNotificationRecord(pkg, AUTOGROUP_AT_COUNT,
1830                 String.valueOf(AUTOGROUP_AT_COUNT), UserHandle.of(7), "testGrp", true);
1831         notificationList.add(r);
1832         mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
1833         verifyNoMoreInteractions(mCallback);
1834     }
1835 
1836     @Test
1837     @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
testNoGroup_summaryWithChildren_forcedGrouping()1838     public void testNoGroup_summaryWithChildren_forcedGrouping() {
1839         final List<NotificationRecord> notificationList = new ArrayList<>();
1840         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
1841         final String pkg = "package";
1842         for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
1843             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
1844                 UserHandle.SYSTEM, "testGrp " + i, true);
1845             notificationList.add(r);
1846             mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
1847         }
1848         // Next posted summary has 1 child => no forced grouping
1849         NotificationRecord summary = getNotificationRecord(pkg, AUTOGROUP_AT_COUNT,
1850             String.valueOf(AUTOGROUP_AT_COUNT), UserHandle.SYSTEM, "testGrp", true);
1851         notificationList.add(summary);
1852         NotificationRecord child = getNotificationRecord(pkg, AUTOGROUP_AT_COUNT + 1,
1853             String.valueOf(AUTOGROUP_AT_COUNT + 1), UserHandle.SYSTEM, "testGrp", false);
1854         notificationList.add(child);
1855         mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
1856         verifyNoMoreInteractions(mCallback);
1857     }
1858 
1859     @Test
1860     @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
testNoGroup_groupWithSummary_forcedGrouping()1861     public void testNoGroup_groupWithSummary_forcedGrouping() {
1862         final List<NotificationRecord> notificationList = new ArrayList<>();
1863         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
1864         final String pkg = "package";
1865         for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
1866             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
1867                 UserHandle.SYSTEM, "testGrp " + i, true);
1868             notificationList.add(r);
1869             mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
1870         }
1871         // Next posted notification has summary => no forced grouping
1872         NotificationRecord summary = getNotificationRecord(pkg, AUTOGROUP_AT_COUNT,
1873             String.valueOf(AUTOGROUP_AT_COUNT), UserHandle.SYSTEM, "testGrp", true);
1874         notificationList.add(summary);
1875         NotificationRecord child = getNotificationRecord(pkg, AUTOGROUP_AT_COUNT + 1,
1876             String.valueOf(AUTOGROUP_AT_COUNT + 1), UserHandle.SYSTEM, "testGrp", false);
1877         notificationList.add(child);
1878         summaryByGroup.put(summary.getGroupKey(), summary);
1879         mGroupHelper.onNotificationPostedWithDelay(child, notificationList, summaryByGroup);
1880         verifyNoMoreInteractions(mCallback);
1881     }
1882 
1883     @Test
1884     @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
testAddAggregateSummary_summaryNoChildren()1885     public void testAddAggregateSummary_summaryNoChildren() {
1886         final String pkg = "package";
1887         final String expectedGroupKey = GroupHelper.getFullAggregateGroupKey(pkg,
1888                 AGGREGATE_GROUP_KEY + "AlertingSection", UserHandle.SYSTEM.getIdentifier());
1889         final List<NotificationRecord> notificationList = new ArrayList<>();
1890         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
1891         // Post group summaries without children => force autogroup
1892         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
1893             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
1894                 UserHandle.SYSTEM, "testGrp " + i, true);
1895             notificationList.add(r);
1896             mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
1897         }
1898         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
1899             eq(expectedGroupKey), anyInt(), eq(getNotificationAttributes(BASE_FLAGS)));
1900         verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(),
1901             eq(expectedGroupKey), eq(true));
1902         verify(mCallback, never()).removeAutoGroup(anyString());
1903         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
1904         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
1905             any());
1906     }
1907 
1908     @Test
1909     @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
testAddAggregateSummary_childrenNoSummary()1910     public void testAddAggregateSummary_childrenNoSummary() {
1911         final String pkg = "package";
1912         final String expectedGroupKey = GroupHelper.getFullAggregateGroupKey(pkg,
1913                 AGGREGATE_GROUP_KEY + "AlertingSection", UserHandle.SYSTEM.getIdentifier());
1914         final List<NotificationRecord> notificationList = new ArrayList<>();
1915         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
1916         // Post group notifications without summaries => force autogroup
1917         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
1918             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
1919                 UserHandle.SYSTEM, "testGrp " + i, false);
1920             notificationList.add(r);
1921             mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
1922         }
1923         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
1924                 eq(expectedGroupKey), anyInt(), eq(getNotificationAttributes(BASE_FLAGS)));
1925         verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(),
1926                 eq(expectedGroupKey), eq(true));
1927         verify(mCallback, never()).removeAutoGroup(anyString());
1928         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
1929         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
1930                 any());
1931     }
1932 
1933     @Test
1934     @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
testAddAggregateSummary_multipleSections()1935     public void testAddAggregateSummary_multipleSections() {
1936         final String pkg = "package";
1937         final String expectedGroupKey_alerting = GroupHelper.getFullAggregateGroupKey(pkg,
1938             AGGREGATE_GROUP_KEY + "AlertingSection", UserHandle.SYSTEM.getIdentifier());
1939         final String expectedGroupKey_silent = GroupHelper.getFullAggregateGroupKey(pkg,
1940             AGGREGATE_GROUP_KEY + "SilentSection", UserHandle.SYSTEM.getIdentifier());
1941 
1942         final List<NotificationRecord> notificationList = new ArrayList<>();
1943         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
1944         // Post notifications with different importance values => force group into separate sections
1945         NotificationRecord r;
1946         for (int i = 0; i < 2 * AUTOGROUP_AT_COUNT; i++) {
1947             if (i % 2 == 0) {
1948                 r = getNotificationRecord(pkg, i, String.valueOf(i), UserHandle.SYSTEM,
1949                     "testGrp " + i, true, IMPORTANCE_DEFAULT);
1950             } else {
1951                 r = getNotificationRecord(pkg, i, String.valueOf(i), UserHandle.SYSTEM,
1952                     "testGrp " + i, false, IMPORTANCE_LOW);
1953             }
1954             notificationList.add(r);
1955             mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
1956         }
1957         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
1958             eq(expectedGroupKey_alerting), anyInt(), eq(getNotificationAttributes(BASE_FLAGS)));
1959         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
1960             eq(expectedGroupKey_silent), anyInt(), eq(getNotificationAttributes(BASE_FLAGS)));
1961 
1962         verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(),
1963                 eq(expectedGroupKey_alerting), eq(true));
1964         verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(),
1965             eq(expectedGroupKey_silent), eq(true));
1966 
1967         verify(mCallback, never()).removeAutoGroup(anyString());
1968         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
1969     }
1970 
1971 
1972     @Test
1973     @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
1974     @DisableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
testAddAggregateSummary_mixUngroupedAndAbusive_alwaysAutogroup()1975     public void testAddAggregateSummary_mixUngroupedAndAbusive_alwaysAutogroup() {
1976         final String pkg = "package";
1977         final String expectedGroupKey = GroupHelper.getFullAggregateGroupKey(pkg,
1978             AGGREGATE_GROUP_KEY + "AlertingSection", UserHandle.SYSTEM.getIdentifier());
1979         // Post ungrouped notifications => create autogroup
1980         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
1981             mGroupHelper.onNotificationPosted(
1982                 getNotificationRecord(pkg, i, String.valueOf(i), UserHandle.SYSTEM), false);
1983         }
1984         verify(mCallback, times(1)).addAutoGroupSummary(
1985                 anyInt(), eq(pkg), anyString(), eq(expectedGroupKey),
1986                 anyInt(), eq(getNotificationAttributes(BASE_FLAGS)));
1987         verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(), eq(expectedGroupKey),
1988                 anyBoolean());
1989         verify(mCallback, never()).removeAutoGroup(anyString());
1990         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
1991         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
1992                 any());
1993 
1994         reset(mCallback);
1995 
1996         // Post group notifications without summaries => add to autogroup
1997         final List<NotificationRecord> notificationList = new ArrayList<>();
1998         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
1999         final int id = AUTOGROUP_AT_COUNT;
2000         NotificationRecord r = getNotificationRecord(pkg, id, String.valueOf(id),
2001                 UserHandle.SYSTEM, "testGrp " + id, false);
2002         notificationList.add(r);
2003         mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
2004 
2005         // Check that the new notification was added
2006         verify(mCallback, times(1)).addAutoGroup(eq(r.getKey()),
2007                 eq(expectedGroupKey), eq(true));
2008         verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), eq(pkg),
2009                 eq(expectedGroupKey), any());
2010         verify(mCallback, never()).addAutoGroupSummary(anyInt(), anyString(), anyString(),
2011                 anyString(), anyInt(), any());
2012         verify(mCallback, never()).removeAutoGroup(anyString());
2013         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
2014     }
2015 
2016     @Test
2017     @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
2018     @DisableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
testUpdateAggregateSummary_postUngroupedAfterForcedGrouping_alwaysAutogroup()2019     public void testUpdateAggregateSummary_postUngroupedAfterForcedGrouping_alwaysAutogroup() {
2020         final String pkg = "package";
2021         final String expectedGroupKey = GroupHelper.getFullAggregateGroupKey(pkg,
2022             AGGREGATE_GROUP_KEY + "AlertingSection", UserHandle.SYSTEM.getIdentifier());
2023         final List<NotificationRecord> notificationList = new ArrayList<>();
2024         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
2025         // Post group notifications without summaries => force autogroup
2026         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
2027             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
2028                 UserHandle.SYSTEM, "testGrp " + i, false);
2029             notificationList.add(r);
2030             mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
2031         }
2032         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
2033             eq(expectedGroupKey), anyInt(), eq(getNotificationAttributes(BASE_FLAGS)));
2034         verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(),
2035             eq(expectedGroupKey), eq(true));
2036         verify(mCallback, never()).removeAutoGroup(anyString());
2037         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
2038         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
2039             any());
2040 
2041         reset(mCallback);
2042 
2043         // Post ungrouped notification => update autogroup
2044         final int id = AUTOGROUP_AT_COUNT;
2045         NotificationRecord r = getNotificationRecord(pkg, id, String.valueOf(id),
2046                 UserHandle.SYSTEM);
2047         mGroupHelper.onNotificationPosted(r, true);
2048 
2049         verify(mCallback, times(1)).addAutoGroup(eq(r.getKey()),
2050                 eq(expectedGroupKey), eq(true));
2051         verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), eq(pkg),
2052                 eq(expectedGroupKey), eq(getNotificationAttributes(BASE_FLAGS)));
2053         verify(mCallback, never()).addAutoGroupSummary(anyInt(), anyString(), anyString(),
2054                 anyString(), anyInt(), any());
2055         verify(mCallback, never()).removeAutoGroup(anyString());
2056         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
2057     }
2058 
2059     @Test
2060     @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
2061             android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST})
testUpdateAggregateSummary_postUngroupedAfterForcedGrouping()2062     public void testUpdateAggregateSummary_postUngroupedAfterForcedGrouping() {
2063         final String pkg = "package";
2064         final String expectedGroupKey = GroupHelper.getFullAggregateGroupKey(pkg,
2065             AGGREGATE_GROUP_KEY + "AlertingSection", UserHandle.SYSTEM.getIdentifier());
2066         final List<NotificationRecord> notificationList = new ArrayList<>();
2067         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
2068         // Post group notifications without summaries => force autogroup
2069         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
2070             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
2071                 UserHandle.SYSTEM, "testGrp " + i, false);
2072             notificationList.add(r);
2073             mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
2074         }
2075         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
2076             eq(expectedGroupKey), anyInt(), eq(getNotificationAttributes(BASE_FLAGS)));
2077         verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(),
2078             eq(expectedGroupKey), eq(true));
2079         verify(mCallback, never()).removeAutoGroup(anyString());
2080         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
2081         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
2082             any());
2083 
2084         reset(mCallback);
2085 
2086         // Post ungrouped notification => update autogroup
2087         final int id = AUTOGROUP_AT_COUNT;
2088         NotificationRecord r = getNotificationRecord(pkg, id, String.valueOf(id),
2089             UserHandle.SYSTEM);
2090         mGroupHelper.onNotificationPosted(r, true);
2091 
2092         verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), eq(pkg),
2093             eq(expectedGroupKey), eq(getNotificationAttributes(BASE_FLAGS)));
2094         verify(mCallback, never()).addAutoGroup(anyString(), anyString(), anyBoolean());
2095         verify(mCallback, never()).addAutoGroupSummary(anyInt(), anyString(), anyString(),
2096             anyString(), anyInt(), any());
2097         verify(mCallback, never()).removeAutoGroup(anyString());
2098         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
2099     }
2100 
2101     @Test
2102     @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
testUpdateAggregateSummary_postAfterForcedGrouping()2103     public void testUpdateAggregateSummary_postAfterForcedGrouping() {
2104         final String pkg = "package";
2105         final String expectedGroupKey = GroupHelper.getFullAggregateGroupKey(pkg,
2106             AGGREGATE_GROUP_KEY + "AlertingSection", UserHandle.SYSTEM.getIdentifier());
2107         final List<NotificationRecord> notificationList = new ArrayList<>();
2108         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
2109         // Post group notifications w/o summaries and summaries w/o children => force autogrouping
2110         NotificationRecord r;
2111         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
2112             if (i % 2 == 0) {
2113                 r = getNotificationRecord(pkg, i, String.valueOf(i), UserHandle.SYSTEM,
2114                     "testGrp " + i, true);
2115             } else {
2116                 r = getNotificationRecord(pkg, i, String.valueOf(i), UserHandle.SYSTEM,
2117                     "testGrp " + i, false);
2118             }
2119             notificationList.add(r);
2120             mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
2121         }
2122 
2123         // Post another notification after forced grouping
2124         final Icon icon = mock(Icon.class);
2125         when(icon.sameAs(icon)).thenReturn(true);
2126         final int iconColor = Color.BLUE;
2127         r = getNotificationRecord(
2128                 getSbn(pkg, AUTOGROUP_AT_COUNT, String.valueOf(AUTOGROUP_AT_COUNT),
2129                     UserHandle.SYSTEM, "testGrp " + AUTOGROUP_AT_COUNT, icon, iconColor));
2130 
2131         notificationList.add(r);
2132         mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
2133 
2134         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
2135             eq(expectedGroupKey), anyInt(), eq(getNotificationAttributes(BASE_FLAGS)));
2136         verify(mCallback, times(AUTOGROUP_AT_COUNT + 1)).addAutoGroup(anyString(),
2137             eq(expectedGroupKey), eq(true));
2138         verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), eq(pkg),
2139             eq(expectedGroupKey), any());
2140         verify(mCallback, never()).removeAutoGroup(anyString());
2141         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
2142     }
2143 
2144     @Test
2145     @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
testRemoveAggregateSummary_removeAllNotifications()2146     public void testRemoveAggregateSummary_removeAllNotifications() {
2147         final String pkg = "package";
2148         final String expectedGroupKey = GroupHelper.getFullAggregateGroupKey(pkg,
2149             AGGREGATE_GROUP_KEY + "AlertingSection", UserHandle.SYSTEM.getIdentifier());
2150         final List<NotificationRecord> notificationList = new ArrayList<>();
2151         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
2152         // Post group notifications without summaries => force autogroup
2153         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
2154             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
2155                 UserHandle.SYSTEM, "testGrp " + i, false);
2156             notificationList.add(r);
2157             mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
2158         }
2159         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
2160                 eq(expectedGroupKey), anyInt(), eq(getNotificationAttributes(BASE_FLAGS)));
2161         verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(),
2162                 eq(expectedGroupKey), eq(true));
2163         verify(mCallback, never()).removeAutoGroup(anyString());
2164         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
2165         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
2166                 any());
2167         Mockito.reset(mCallback);
2168 
2169         // Remove all posted notifications
2170         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
2171             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
2172                     UserHandle.SYSTEM, "testGrp " + i, false);
2173             r.setOverrideGroupKey(expectedGroupKey);
2174             mGroupHelper.onNotificationRemoved(r, notificationList, false);
2175         }
2176         // Check that the autogroup summary is removed
2177         verify(mCallback, times(1)).removeAutoGroupSummary(anyInt(), eq(pkg),
2178                 eq(expectedGroupKey));
2179     }
2180 
2181     @Test
2182     @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS})
testRemoveChildNotification_summaryForceGrouped()2183     public void testRemoveChildNotification_summaryForceGrouped() {
2184         // Check that removing all child notifications from a group will trigger empty summary
2185         // force grouping re-evaluation
2186         final List<NotificationRecord> notificationList = new ArrayList<>();
2187         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
2188         final String pkg = "package";
2189         // Post summaries without children, below the force grouping limit
2190         for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
2191             NotificationRecord summary = getNotificationRecord(pkg, i + 42, String.valueOf(i + 42),
2192                     UserHandle.SYSTEM, "testGrp " + i, true);
2193             notificationList.add(summary);
2194             mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
2195         }
2196         // Post a valid (full) group
2197         final int summaryId = 4242;
2198         final int numChildren = 3;
2199         final ArrayList<NotificationRecord> childrenToRemove = new ArrayList<>();
2200         NotificationRecord summary = getNotificationRecord(pkg, summaryId,
2201                 String.valueOf(summaryId), UserHandle.SYSTEM, "testGrp " + summaryId, true);
2202         notificationList.add(summary);
2203         summaryByGroup.put(summary.getGroupKey(), summary);
2204         for (int i = 0; i < numChildren; i++) {
2205             NotificationRecord child = getNotificationRecord(pkg, summaryId + 42,
2206                     String.valueOf(i + 42), UserHandle.SYSTEM, "testGrp " + summaryId, false);
2207             notificationList.add(child);
2208             // schedule all children for removal
2209             childrenToRemove.add(child);
2210         }
2211         mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
2212         verifyNoMoreInteractions(mCallback);
2213 
2214         // Remove all child notifications from the valid group => summary without children
2215         Mockito.reset(mCallback);
2216         for (NotificationRecord r: childrenToRemove) {
2217             notificationList.remove(r);
2218             mGroupHelper.onNotificationRemoved(r, notificationList, false);
2219         }
2220         // Only call onGroupedNotificationRemovedWithDelay with the summary notification
2221         mGroupHelper.onGroupedNotificationRemovedWithDelay(summary, notificationList,
2222                 summaryByGroup);
2223 
2224         // Check that the summaries were force grouped
2225         final String expectedGroupKey = GroupHelper.getFullAggregateGroupKey(pkg,
2226                 AGGREGATE_GROUP_KEY + "AlertingSection", UserHandle.SYSTEM.getIdentifier());
2227         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
2228                 eq(expectedGroupKey), anyInt(), eq(getNotificationAttributes(BASE_FLAGS)));
2229         verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(),
2230                 eq(expectedGroupKey), eq(true));
2231         verify(mCallback, never()).removeAutoGroup(anyString());
2232         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
2233         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
2234                 any());
2235     }
2236 
2237     @Test
2238     @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS})
testRemoveChildNotification_groupBecomesSingleton()2239     public void testRemoveChildNotification_groupBecomesSingleton() {
2240         // Check that removing child notifications from a group will trigger singleton force
2241         // grouping re-evaluation
2242         final List<NotificationRecord> notificationList = new ArrayList<>();
2243         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
2244         final String pkg = "package";
2245         // Post singleton groups, under forced group limit
2246         for (int i = 0; i < AUTOGROUP_SINGLETONS_AT_COUNT - 1; i++) {
2247             NotificationRecord summary = getNotificationRecord(pkg, i,
2248                     String.valueOf(i), UserHandle.SYSTEM, "testGrp " + i, true);
2249             notificationList.add(summary);
2250             NotificationRecord child = getNotificationRecord(pkg, i + 42,
2251                     String.valueOf(i + 42), UserHandle.SYSTEM, "testGrp " + i, false);
2252             notificationList.add(child);
2253             summaryByGroup.put(summary.getGroupKey(), summary);
2254             mGroupHelper.onNotificationPostedWithDelay(child, notificationList, summaryByGroup);
2255             mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
2256         }
2257         // Post a valid (full) group
2258         final int summaryId = 4242;
2259         final int numChildren = 3;
2260         final ArrayList<NotificationRecord> childrenToRemove = new ArrayList<>();
2261         NotificationRecord summary = getNotificationRecord(pkg, summaryId,
2262                 String.valueOf(summaryId), UserHandle.SYSTEM, "testGrp " + summaryId, true);
2263         notificationList.add(summary);
2264         summaryByGroup.put(summary.getGroupKey(), summary);
2265         for (int i = 0; i < numChildren; i++) {
2266             NotificationRecord child = getNotificationRecord(pkg, summaryId + 42,
2267                     String.valueOf(i + 42), UserHandle.SYSTEM, "testGrp " + summaryId, false);
2268             notificationList.add(child);
2269 
2270             // schedule all children except one for removal
2271             if (i < numChildren - 1) {
2272                 childrenToRemove.add(child);
2273             }
2274         }
2275         mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
2276         verifyNoMoreInteractions(mCallback);
2277 
2278         // Remove some child notifications from the valid group, transform into a singleton group
2279         Mockito.reset(mCallback);
2280         for (NotificationRecord r: childrenToRemove) {
2281             notificationList.remove(r);
2282             mGroupHelper.onNotificationRemoved(r, notificationList, false);
2283         }
2284         // Only call onGroupedNotificationRemovedWithDelay with the summary notification
2285         mGroupHelper.onGroupedNotificationRemovedWithDelay(summary, notificationList,
2286                 summaryByGroup);
2287 
2288         // Check that the singleton groups were force grouped
2289         final String expectedGroupKey = GroupHelper.getFullAggregateGroupKey(pkg,
2290                 AGGREGATE_GROUP_KEY + "AlertingSection", UserHandle.SYSTEM.getIdentifier());
2291         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
2292                 eq(expectedGroupKey), anyInt(), eq(getNotificationAttributes(BASE_FLAGS)));
2293         verify(mCallback, times(AUTOGROUP_SINGLETONS_AT_COUNT)).addAutoGroup(anyString(),
2294                 eq(expectedGroupKey), eq(true));
2295         verify(mCallback, never()).removeAutoGroup(anyString());
2296         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
2297         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
2298                 any());
2299         verify(mCallback, times(AUTOGROUP_SINGLETONS_AT_COUNT)).removeAppProvidedSummary(
2300                 anyString());
2301     }
2302 
2303     @Test
2304     @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS})
testRemoveAllGroupNotifications_noForceGrouping()2305     public void testRemoveAllGroupNotifications_noForceGrouping() {
2306         // Check that removing all notifications from a group will not trigger any force grouping
2307         // re-evaluation
2308         final List<NotificationRecord> notificationList = new ArrayList<>();
2309         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
2310         final String pkg = "package";
2311         // Post summaries without children, below the force grouping limit
2312         for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
2313             NotificationRecord summary = getNotificationRecord(pkg, i + 42, String.valueOf(i + 42),
2314                     UserHandle.SYSTEM, "testGrp " + i, true);
2315             notificationList.add(summary);
2316             mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
2317         }
2318         // Post a valid (full) group
2319         final int summaryId = 4242;
2320         final int numChildren = 3;
2321         final String groupToRemove = "testRemoveGrp";
2322         NotificationRecord summary = getNotificationRecord(pkg, summaryId,
2323                 String.valueOf(summaryId), UserHandle.SYSTEM, groupToRemove + summaryId, true);
2324         notificationList.add(summary);
2325         summaryByGroup.put(summary.getGroupKey(), summary);
2326         for (int i = 0; i < numChildren; i++) {
2327             NotificationRecord child = getNotificationRecord(pkg, summaryId + 42,
2328                     String.valueOf(i + 42), UserHandle.SYSTEM, groupToRemove + summaryId, false);
2329             notificationList.add(child);
2330         }
2331         mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
2332         verifyNoMoreInteractions(mCallback);
2333 
2334         // Remove all child notifications from the valid group => summary without children
2335         Mockito.reset(mCallback);
2336         for (NotificationRecord r: notificationList) {
2337             if (r.getGroupKey().contains(groupToRemove)) {
2338                 r.isCanceled = true;
2339                 mGroupHelper.onNotificationRemoved(r, notificationList, false);
2340             }
2341         }
2342         // Only call onGroupedNotificationRemovedWithDelay with the summary notification
2343         mGroupHelper.onGroupedNotificationRemovedWithDelay(summary, notificationList,
2344                 summaryByGroup);
2345         // Check that nothing was force grouped
2346         verifyNoMoreInteractions(mCallback);
2347     }
2348 
2349     @Test
2350     @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS})
testUpdateToUngroupableSection_cleanupUngrouped()2351     public void testUpdateToUngroupableSection_cleanupUngrouped() {
2352         final String pkg = "package";
2353         // Post notification w/o group in a valid section
2354         NotificationRecord notification = spy(getNotificationRecord(pkg, 0, "", mUser,
2355                 "", false, IMPORTANCE_LOW));
2356         Notification n = mock(Notification.class);
2357         StatusBarNotification sbn = spy(getSbn(pkg, 0, "0", UserHandle.SYSTEM));
2358         when(notification.getNotification()).thenReturn(n);
2359         when(notification.getSbn()).thenReturn(sbn);
2360         when(sbn.getNotification()).thenReturn(n);
2361         when(n.isStyle(Notification.CallStyle.class)).thenReturn(false);
2362         assertThat(GroupHelper.getSection(notification)).isNotNull();
2363         mGroupHelper.onNotificationPosted(notification, false);
2364 
2365         // Update notification to invalid section
2366         when(n.isStyle(Notification.CallStyle.class)).thenReturn(true);
2367         assertThat(GroupHelper.getSection(notification)).isNull();
2368         boolean needsAutogrouping = mGroupHelper.onNotificationPosted(notification, false);
2369         assertThat(needsAutogrouping).isFalse();
2370 
2371         // Check that GH internal state (ungrouped list) was cleaned-up
2372         // Post AUTOGROUP_AT_COUNT-1 notifications => should not autogroup
2373         for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
2374             int id = 42 + i;
2375             notification = getNotificationRecord(pkg, id, "" + id, mUser,
2376                 null, false, IMPORTANCE_LOW);
2377             mGroupHelper.onNotificationPosted(notification, false);
2378         }
2379 
2380         verify(mCallback, never()).addAutoGroupSummary(anyInt(), anyString(), anyString(),
2381                 anyString(), anyInt(), any());
2382         verify(mCallback, never()).addAutoGroup(anyString(), anyString(), anyBoolean());
2383     }
2384 
2385     @Test
2386     @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS,
2387             android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST})
testUpdateToUngroupableSection_afterAutogroup_isUngrouped()2388     public void testUpdateToUngroupableSection_afterAutogroup_isUngrouped() {
2389         final String pkg = "package";
2390         final List<NotificationRecord> notificationList = new ArrayList<>();
2391         // Post notification w/o group in a valid section
2392         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
2393             NotificationRecord notification = spy(getNotificationRecord(pkg, i, "" + i, mUser,
2394                     "", false, IMPORTANCE_LOW));
2395             Notification n = mock(Notification.class);
2396             StatusBarNotification sbn = spy(getSbn(pkg, i, "" + i, UserHandle.SYSTEM));
2397             when(notification.getNotification()).thenReturn(n);
2398             when(notification.getSbn()).thenReturn(sbn);
2399             when(sbn.getNotification()).thenReturn(n);
2400             when(n.isStyle(Notification.CallStyle.class)).thenReturn(false);
2401             assertThat(GroupHelper.getSection(notification)).isNotNull();
2402             mGroupHelper.onNotificationPosted(notification, false);
2403             notificationList.add(notification);
2404         }
2405 
2406         final String expectedGroupKey = GroupHelper.getFullAggregateGroupKey(pkg,
2407                 AGGREGATE_GROUP_KEY + "SilentSection", UserHandle.SYSTEM.getIdentifier());
2408         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
2409                 eq(expectedGroupKey), anyInt(), any());
2410         verify(mCallback, times(AUTOGROUP_AT_COUNT - 1)).addAutoGroup(anyString(),
2411                 eq(expectedGroupKey), eq(true));
2412 
2413         // Update a notification to invalid section
2414         Mockito.reset(mCallback);
2415         final NotificationRecord notifToInvalidate = notificationList.get(0);
2416         when(notifToInvalidate.getNotification().isStyle(Notification.CallStyle.class)).thenReturn(
2417                 true);
2418         assertThat(GroupHelper.getSection(notifToInvalidate)).isNull();
2419         boolean needsAutogrouping = mGroupHelper.onNotificationPosted(notifToInvalidate, true);
2420         assertThat(needsAutogrouping).isFalse();
2421 
2422         // Check that the updated notification was removed from the autogroup
2423         verify(mCallback, times(1)).removeAutoGroup(eq(notifToInvalidate.getKey()));
2424         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
2425         verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), anyString(),
2426                 eq(expectedGroupKey), any());
2427     }
2428 
2429     @Test
2430     @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS,
2431             android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST})
testUpdateToUngroupableSection_onRemoved_isUngrouped()2432     public void testUpdateToUngroupableSection_onRemoved_isUngrouped() {
2433         final String pkg = "package";
2434         final List<NotificationRecord> notificationList = new ArrayList<>();
2435         // Post notification w/o group in a valid section
2436         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
2437             NotificationRecord notification = spy(getNotificationRecord(pkg, i, "" + i, mUser,
2438                     "", false, IMPORTANCE_LOW));
2439             Notification n = mock(Notification.class);
2440             StatusBarNotification sbn = spy(getSbn(pkg, i, "" + i, UserHandle.SYSTEM));
2441             when(notification.getNotification()).thenReturn(n);
2442             when(notification.getSbn()).thenReturn(sbn);
2443             when(sbn.getNotification()).thenReturn(n);
2444             when(n.isStyle(Notification.CallStyle.class)).thenReturn(false);
2445             assertThat(GroupHelper.getSection(notification)).isNotNull();
2446             mGroupHelper.onNotificationPosted(notification, false);
2447             notificationList.add(notification);
2448         }
2449 
2450         final String expectedGroupKey = GroupHelper.getFullAggregateGroupKey(pkg,
2451                 AGGREGATE_GROUP_KEY + "SilentSection", UserHandle.SYSTEM.getIdentifier());
2452         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
2453                 eq(expectedGroupKey), anyInt(), any());
2454         verify(mCallback, times(AUTOGROUP_AT_COUNT - 1)).addAutoGroup(anyString(),
2455                 eq(expectedGroupKey), eq(true));
2456 
2457         // Update a notification to invalid section and removed it
2458         Mockito.reset(mCallback);
2459         final NotificationRecord notifToInvalidate = notificationList.get(0);
2460         when(notifToInvalidate.getNotification().isStyle(Notification.CallStyle.class)).thenReturn(
2461                 true);
2462         assertThat(GroupHelper.getSection(notifToInvalidate)).isNull();
2463         notificationList.remove(notifToInvalidate);
2464         mGroupHelper.onNotificationRemoved(notifToInvalidate, notificationList, false);
2465 
2466         // Check that the autogroup was updated
2467         verify(mCallback, never()).removeAutoGroup(anyString());
2468         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
2469         verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), anyString(),
2470                 eq(expectedGroupKey), any());
2471     }
2472 
2473     @Test
2474     @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS})
testUpdateToUngroupableSection_afterForceGrouping_isUngrouped()2475     public void testUpdateToUngroupableSection_afterForceGrouping_isUngrouped() {
2476         final String pkg = "package";
2477         final String groupName = "testGroup";
2478         final List<NotificationRecord> notificationList = new ArrayList<>();
2479         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
2480         // Post valid section summary notifications without children => force group
2481         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
2482             NotificationRecord notification = spy(getNotificationRecord(mPkg, i, "" + i, mUser,
2483                     groupName, true, IMPORTANCE_LOW));
2484             Notification n = mock(Notification.class);
2485             StatusBarNotification sbn = spy(getSbn(pkg, i, "" + i, UserHandle.SYSTEM, groupName));
2486             when(notification.getNotification()).thenReturn(n);
2487             when(notification.getSbn()).thenReturn(sbn);
2488             when(n.getGroup()).thenReturn(groupName);
2489             when(sbn.getNotification()).thenReturn(n);
2490             when(n.isStyle(Notification.CallStyle.class)).thenReturn(false);
2491             assertThat(GroupHelper.getSection(notification)).isNotNull();
2492             notificationList.add(notification);
2493             mGroupHelper.onNotificationPostedWithDelay(notification, notificationList,
2494                     summaryByGroup);
2495         }
2496 
2497         final String expectedGroupKey = GroupHelper.getFullAggregateGroupKey(pkg,
2498                 AGGREGATE_GROUP_KEY + "SilentSection", UserHandle.SYSTEM.getIdentifier());
2499         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
2500                 eq(expectedGroupKey), anyInt(), any());
2501         verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(),
2502                 eq(expectedGroupKey), eq(true));
2503 
2504         // Update a notification to invalid section
2505         Mockito.reset(mCallback);
2506         final NotificationRecord notifToInvalidate = notificationList.get(0);
2507         when(notifToInvalidate.getNotification().isStyle(Notification.CallStyle.class)).thenReturn(
2508                 true);
2509         assertThat(GroupHelper.getSection(notifToInvalidate)).isNull();
2510         boolean needsAutogrouping = mGroupHelper.onNotificationPosted(notifToInvalidate, true);
2511         assertThat(needsAutogrouping).isFalse();
2512 
2513         // Check that GH internal state (ungrouped list) was cleaned-up
2514         verify(mCallback, times(1)).removeAutoGroup(eq(notifToInvalidate.getKey()));
2515         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
2516         verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), anyString(),
2517                 eq(expectedGroupKey), any());
2518     }
2519 
2520     @Test
2521     @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS,
2522             android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST})
testRepostWithNewChannel_afterAutogrouping_isRegrouped()2523     public void testRepostWithNewChannel_afterAutogrouping_isRegrouped() {
2524         final String pkg = "package";
2525         final List<NotificationRecord> notificationList = new ArrayList<>();
2526         // Post ungrouped notifications => will be autogrouped
2527         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
2528             NotificationRecord notification = getNotificationRecord(pkg, i + 42,
2529                     String.valueOf(i + 42), UserHandle.SYSTEM, null, false);
2530             notificationList.add(notification);
2531             mGroupHelper.onNotificationPosted(notification, false);
2532         }
2533 
2534         final String expectedGroupKey = GroupHelper.getFullAggregateGroupKey(pkg,
2535                 AGGREGATE_GROUP_KEY + "AlertingSection", UserHandle.SYSTEM.getIdentifier());
2536         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
2537                 eq(expectedGroupKey), anyInt(), any());
2538         verify(mCallback, times(AUTOGROUP_AT_COUNT - 1)).addAutoGroup(anyString(),
2539                 eq(expectedGroupKey), eq(true));
2540 
2541         // Post ungrouped notifications to a different section, below autogroup limit
2542         Mockito.reset(mCallback);
2543         // Post ungrouped notifications => will be autogrouped
2544         final NotificationChannel silentChannel = new NotificationChannel("TEST_CHANNEL_ID1",
2545                 "TEST_CHANNEL_ID1", IMPORTANCE_LOW);
2546         for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
2547             NotificationRecord notification = getNotificationRecord(pkg, i + 4242,
2548                     String.valueOf(i + 4242), UserHandle.SYSTEM, null, false, silentChannel);
2549             notificationList.add(notification);
2550             mGroupHelper.onNotificationPosted(notification, false);
2551         }
2552 
2553         verify(mCallback, never()).addAutoGroupSummary(anyInt(), anyString(), anyString(),
2554                 anyString(), anyInt(), any());
2555         verify(mCallback, never()).addAutoGroup(anyString(), anyString(), anyBoolean());
2556 
2557         // Update a notification to a different channel that moves it to a different section
2558         Mockito.reset(mCallback);
2559         final NotificationRecord notifToInvalidate = notificationList.get(0);
2560         final NotificationSectioner initialSection = GroupHelper.getSection(notifToInvalidate);
2561         final NotificationChannel updatedChannel = new NotificationChannel("TEST_CHANNEL_ID2",
2562                 "TEST_CHANNEL_ID2", IMPORTANCE_LOW);
2563         notifToInvalidate.updateNotificationChannel(updatedChannel);
2564         assertThat(GroupHelper.getSection(notifToInvalidate)).isNotEqualTo(initialSection);
2565         boolean needsAutogrouping = mGroupHelper.onNotificationPosted(notifToInvalidate, false);
2566         assertThat(needsAutogrouping).isTrue();
2567 
2568         // Check that the silent section was autogrouped
2569         final String silentSectionGroupKey = GroupHelper.getFullAggregateGroupKey(pkg,
2570                 AGGREGATE_GROUP_KEY + "SilentSection", UserHandle.SYSTEM.getIdentifier());
2571         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
2572                 eq(silentSectionGroupKey), anyInt(), any());
2573         verify(mCallback, times(AUTOGROUP_AT_COUNT - 1)).addAutoGroup(anyString(),
2574                 eq(silentSectionGroupKey), eq(true));
2575         verify(mCallback, times(1)).removeAutoGroup(eq(notifToInvalidate.getKey()));
2576         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
2577         verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), anyString(),
2578                 eq(expectedGroupKey), any());
2579     }
2580 
2581     @Test
2582     @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS,
2583             android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST})
testRepostWithNewChannel_afterForceGrouping_isRegrouped()2584     public void testRepostWithNewChannel_afterForceGrouping_isRegrouped() {
2585         final String pkg = "package";
2586         final String groupName = "testGroup";
2587         final List<NotificationRecord> notificationList = new ArrayList<>();
2588         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
2589         // Post valid section summary notifications without children => force group
2590         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
2591             NotificationRecord notification = getNotificationRecord(pkg, i + 42,
2592                     String.valueOf(i + 42), UserHandle.SYSTEM, groupName, false);
2593             notificationList.add(notification);
2594             mGroupHelper.onNotificationPostedWithDelay(notification, notificationList,
2595                     summaryByGroup);
2596         }
2597 
2598         final String expectedGroupKey = GroupHelper.getFullAggregateGroupKey(pkg,
2599                 AGGREGATE_GROUP_KEY + "AlertingSection", UserHandle.SYSTEM.getIdentifier());
2600         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
2601                 eq(expectedGroupKey), anyInt(), any());
2602         verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(),
2603                 eq(expectedGroupKey), eq(true));
2604 
2605         // Update a notification to a different channel that moves it to a different section
2606         Mockito.reset(mCallback);
2607         final NotificationRecord notifToInvalidate = notificationList.get(0);
2608         final NotificationSectioner initialSection = GroupHelper.getSection(notifToInvalidate);
2609         final NotificationChannel updatedChannel = new NotificationChannel("TEST_CHANNEL_ID2",
2610                 "TEST_CHANNEL_ID2", IMPORTANCE_LOW);
2611         notifToInvalidate.updateNotificationChannel(updatedChannel);
2612         assertThat(GroupHelper.getSection(notifToInvalidate)).isNotEqualTo(initialSection);
2613         boolean needsAutogrouping = mGroupHelper.onNotificationPosted(notifToInvalidate, false);
2614 
2615         mGroupHelper.onNotificationPostedWithDelay(notifToInvalidate, notificationList,
2616                 summaryByGroup);
2617 
2618         // Check that the updated notification is removed from the autogroup
2619         assertThat(needsAutogrouping).isFalse();
2620         verify(mCallback, times(1)).removeAutoGroup(eq(notifToInvalidate.getKey()));
2621         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
2622         verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), anyString(),
2623                 eq(expectedGroupKey), any());
2624 
2625         // Post child notifications for the silent sectin => will be autogrouped
2626         Mockito.reset(mCallback);
2627         final NotificationChannel silentChannel = new NotificationChannel("TEST_CHANNEL_ID1",
2628                 "TEST_CHANNEL_ID1", IMPORTANCE_LOW);
2629         for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
2630             NotificationRecord notification = getNotificationRecord(pkg, i + 4242,
2631                     String.valueOf(i + 4242), UserHandle.SYSTEM, "aGroup", false, silentChannel);
2632             notificationList.add(notification);
2633             needsAutogrouping = mGroupHelper.onNotificationPosted(notification, false);
2634             assertThat(needsAutogrouping).isFalse();
2635             mGroupHelper.onNotificationPostedWithDelay(notification, notificationList,
2636                     summaryByGroup);
2637         }
2638 
2639         // Check that the silent section was autogrouped
2640         final String silentSectionGroupKey = GroupHelper.getFullAggregateGroupKey(pkg,
2641                 AGGREGATE_GROUP_KEY + "SilentSection", UserHandle.SYSTEM.getIdentifier());
2642         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
2643                 eq(silentSectionGroupKey), anyInt(), any());
2644         verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(),
2645                 eq(silentSectionGroupKey), eq(true));
2646     }
2647 
2648     @Test
2649     @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
testMoveAggregateGroups_updateChannel()2650     public void testMoveAggregateGroups_updateChannel() {
2651         final String pkg = "package";
2652         final String expectedGroupKey_alerting = GroupHelper.getFullAggregateGroupKey(pkg,
2653             AGGREGATE_GROUP_KEY + "AlertingSection", UserHandle.SYSTEM.getIdentifier());
2654         final NotificationChannel channel = new NotificationChannel(TEST_CHANNEL_ID,
2655                 TEST_CHANNEL_ID, IMPORTANCE_DEFAULT);
2656         final List<NotificationRecord> notificationList = new ArrayList<>();
2657         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
2658         // Post group notifications without summaries => force autogroup
2659         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
2660             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
2661                     UserHandle.SYSTEM, "testGrp " + i, false, channel);
2662             notificationList.add(r);
2663             mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
2664         }
2665         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
2666                 eq(expectedGroupKey_alerting), anyInt(), eq(getNotificationAttributes(BASE_FLAGS)));
2667         verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(),
2668                 eq(expectedGroupKey_alerting), eq(true));
2669         verify(mCallback, never()).removeAutoGroup(anyString());
2670         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
2671         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
2672             any());
2673         Mockito.reset(mCallback);
2674 
2675         // Update the channel importance for all posted notifications
2676         final String expectedGroupKey_silent = GroupHelper.getFullAggregateGroupKey(pkg,
2677             AGGREGATE_GROUP_KEY + "SilentSection", UserHandle.SYSTEM.getIdentifier());
2678         channel.setImportance(IMPORTANCE_LOW);
2679         for (NotificationRecord r: notificationList) {
2680             r.updateNotificationChannel(channel);
2681         }
2682         mGroupHelper.onChannelUpdated(UserHandle.SYSTEM.getIdentifier(), pkg, channel,
2683                 notificationList, summaryByGroup);
2684 
2685         // Check that all notifications are moved to the silent section group
2686         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
2687                 eq(expectedGroupKey_silent), anyInt(), eq(getNotificationAttributes(BASE_FLAGS)));
2688         verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(),
2689                 eq(expectedGroupKey_silent), eq(true));
2690 
2691         // Check that the alerting section group is removed
2692         verify(mCallback, times(1)).removeAutoGroupSummary(anyInt(), eq(pkg),
2693                 eq(expectedGroupKey_alerting));
2694         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
2695                 any());
2696     }
2697 
2698     @Test
2699     @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
2700     @DisableFlags(FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION)
testMoveAggregateGroups_updateChannel_multipleChannels()2701     public void testMoveAggregateGroups_updateChannel_multipleChannels() {
2702         final String pkg = "package";
2703         final String expectedGroupKey_alerting = GroupHelper.getFullAggregateGroupKey(pkg,
2704             AGGREGATE_GROUP_KEY + "AlertingSection", UserHandle.SYSTEM.getIdentifier());
2705         final NotificationChannel channel1 = new NotificationChannel("TEST_CHANNEL_ID1",
2706             "TEST_CHANNEL_ID1", IMPORTANCE_DEFAULT);
2707         final NotificationChannel channel2 = new NotificationChannel("TEST_CHANNEL_ID2",
2708             "TEST_CHANNEL_ID2", IMPORTANCE_DEFAULT);
2709         final List<NotificationRecord> notificationList = new ArrayList<>();
2710         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
2711         // Post notifications with different channels that autogroup within the same section
2712         NotificationRecord r;
2713         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
2714             if (i % 2 == 0) {
2715                 r = getNotificationRecord(pkg, i, String.valueOf(i),
2716                         UserHandle.SYSTEM, "testGrp " + i, false, channel1);
2717             } else {
2718                 r = getNotificationRecord(pkg, i, String.valueOf(i),
2719                         UserHandle.SYSTEM, "testGrp " + i, false, channel2);
2720             }
2721             notificationList.add(r);
2722             mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
2723         }
2724         NotificationAttributes expectedSummaryAttr = new NotificationAttributes(BASE_FLAGS,
2725                 mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT,
2726                 "TEST_CHANNEL_ID1");
2727         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
2728                 eq(expectedGroupKey_alerting), anyInt(), eq(expectedSummaryAttr));
2729         verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(),
2730                 eq(expectedGroupKey_alerting), eq(true));
2731         verify(mCallback, never()).removeAutoGroup(anyString());
2732         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
2733         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
2734                 any());
2735         Mockito.reset(mCallback);
2736 
2737         // Update channel1's importance
2738         final String expectedGroupKey_silent = GroupHelper.getFullAggregateGroupKey(pkg,
2739             AGGREGATE_GROUP_KEY + "SilentSection", UserHandle.SYSTEM.getIdentifier());
2740         channel1.setImportance(IMPORTANCE_LOW);
2741         for (NotificationRecord record: notificationList) {
2742             if (record.getChannel().getId().equals(channel1.getId())) {
2743                 record.updateNotificationChannel(channel1);
2744             }
2745         }
2746         mGroupHelper.onChannelUpdated(UserHandle.SYSTEM.getIdentifier(), pkg, channel1,
2747                 notificationList, summaryByGroup);
2748 
2749         // Check that the override group key was cleared
2750         for (NotificationRecord record: notificationList) {
2751             if (record.getChannel().getId().equals(channel1.getId())) {
2752                 assertThat(record.getSbn().getOverrideGroupKey()).isNull();
2753             }
2754         }
2755         // Check that channel1's notifications are moved to the silent section group
2756         // and a group summary is created + notifications are added to the group
2757         verify(mCallback, never()).addAutoGroupSummary(anyInt(), eq(pkg), anyString(), anyString(),
2758                 anyInt(), any());
2759         verify(mCallback, never()).addAutoGroup(anyString(), anyString(), anyBoolean());
2760 
2761         // Check that the alerting section group is not removed, only updated
2762         expectedSummaryAttr = new NotificationAttributes(BASE_FLAGS,
2763             mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT,
2764             "TEST_CHANNEL_ID2");
2765         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), eq(pkg),
2766                 eq(expectedGroupKey_alerting));
2767         verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), eq(pkg),
2768                 eq(expectedGroupKey_alerting), eq(expectedSummaryAttr));
2769     }
2770 
2771     @Test
2772     @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
2773             FLAG_NOTIFICATION_CLASSIFICATION,
2774             FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION})
testMoveAggregateGroups_updateChannel_multipleChannels_regroupOnClassifEnabled()2775     public void testMoveAggregateGroups_updateChannel_multipleChannels_regroupOnClassifEnabled() {
2776         final String pkg = "package";
2777         final String expectedGroupKey_alerting = GroupHelper.getFullAggregateGroupKey(pkg,
2778                 AGGREGATE_GROUP_KEY + "AlertingSection", UserHandle.SYSTEM.getIdentifier());
2779         final int numNotifications = 2 * AUTOGROUP_AT_COUNT;
2780         int numNotificationChannel1 = 0;
2781         final NotificationChannel channel1 = new NotificationChannel("TEST_CHANNEL_ID1",
2782                 "TEST_CHANNEL_ID1", IMPORTANCE_DEFAULT);
2783         final NotificationChannel channel2 = new NotificationChannel("TEST_CHANNEL_ID2",
2784                 "TEST_CHANNEL_ID2", IMPORTANCE_DEFAULT);
2785         final List<NotificationRecord> notificationList = new ArrayList<>();
2786         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
2787         // Post notifications with different channels that autogroup within the same section
2788         NotificationRecord r;
2789         for (int i = 0; i < numNotifications; i++) {
2790             if (i % 2 == 0) {
2791                 r = getNotificationRecord(pkg, i, String.valueOf(i),
2792                         UserHandle.SYSTEM, "testGrp " + i, false, channel1);
2793                 numNotificationChannel1++;
2794             } else {
2795                 r = getNotificationRecord(pkg, i, String.valueOf(i),
2796                         UserHandle.SYSTEM, "testGrp " + i, false, channel2);
2797             }
2798             notificationList.add(r);
2799             mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
2800         }
2801         NotificationAttributes expectedSummaryAttr = new NotificationAttributes(BASE_FLAGS,
2802                 mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT,
2803                 "TEST_CHANNEL_ID1");
2804         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
2805                 eq(expectedGroupKey_alerting), anyInt(), eq(expectedSummaryAttr));
2806         verify(mCallback, times(numNotifications)).addAutoGroup(anyString(),
2807                 eq(expectedGroupKey_alerting), eq(true));
2808         verify(mCallback, never()).removeAutoGroup(anyString());
2809         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
2810         verify(mCallback, times(numNotifications - AUTOGROUP_AT_COUNT)).updateAutogroupSummary(
2811                 anyInt(), anyString(), anyString(), any());
2812         Mockito.reset(mCallback);
2813 
2814         // Update channel1's importance
2815         final String expectedGroupKey_silent = GroupHelper.getFullAggregateGroupKey(pkg,
2816                 AGGREGATE_GROUP_KEY + "SilentSection", UserHandle.SYSTEM.getIdentifier());
2817         channel1.setImportance(IMPORTANCE_LOW);
2818         for (NotificationRecord record: notificationList) {
2819             if (record.getChannel().getId().equals(channel1.getId())) {
2820                 record.updateNotificationChannel(channel1);
2821             }
2822         }
2823         mGroupHelper.onChannelUpdated(UserHandle.SYSTEM.getIdentifier(), pkg, channel1,
2824                 notificationList, summaryByGroup);
2825 
2826         // Check that the override group key was cleared
2827         for (NotificationRecord record: notificationList) {
2828             if (record.getChannel().getId().equals(channel1.getId())) {
2829                 assertThat(record.getSbn().getOverrideGroupKey()).isNull();
2830             }
2831         }
2832         // Check that channel1's notifications are moved to the silent section group
2833         // and a group summary is created + notifications are added to the group
2834         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
2835                 eq(expectedGroupKey_silent), anyInt(), any());
2836         verify(mCallback, times(numNotificationChannel1)).addAutoGroup(anyString(),
2837                 eq(expectedGroupKey_silent), anyBoolean());
2838 
2839         // Check that the alerting section group is not removed, only updated
2840         expectedSummaryAttr = new NotificationAttributes(BASE_FLAGS,
2841                 mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT,
2842                 "TEST_CHANNEL_ID2");
2843         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), eq(pkg),
2844                 eq(expectedGroupKey_alerting));
2845         verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), eq(pkg),
2846                 eq(expectedGroupKey_alerting), eq(expectedSummaryAttr));
2847     }
2848 
2849     @Test
2850     @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
2851             FLAG_NOTIFICATION_CLASSIFICATION,
2852             FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION})
testMoveSections_notificationBundled()2853     public void testMoveSections_notificationBundled() {
2854         final List<NotificationRecord> notificationList = new ArrayList<>();
2855         final String pkg = "package";
2856         final int summaryId = 0;
2857         final int numChildNotif = 2 * AUTOGROUP_AT_COUNT;
2858 
2859         // Create an app-provided group: summary + child notifications
2860         final NotificationChannel channel1 = new NotificationChannel("TEST_CHANNEL_ID1",
2861                 "TEST_CHANNEL_ID1", IMPORTANCE_DEFAULT);
2862         NotificationRecord summary = getNotificationRecord(pkg, summaryId,
2863                 String.valueOf(summaryId), UserHandle.SYSTEM, "testGrp " + summaryId,
2864                 true, channel1);
2865         notificationList.add(summary);
2866         final String originalAppGroupKey = summary.getGroupKey();
2867         for (int i = 0; i < numChildNotif; i++) {
2868             NotificationRecord child = getNotificationRecord(pkg, i + 42, String.valueOf(i + 42),
2869                     UserHandle.SYSTEM, "testGrp " + summaryId, false, channel1);
2870             notificationList.add(child);
2871         }
2872 
2873         // Classify/bundle child notifications
2874         final NotificationChannel socialChannel = new NotificationChannel(
2875                 NotificationChannel.SOCIAL_MEDIA_ID, NotificationChannel.SOCIAL_MEDIA_ID,
2876                 IMPORTANCE_LOW);
2877         final String expectedGroupKey_social = GroupHelper.getFullAggregateGroupKey(pkg,
2878                 AGGREGATE_GROUP_KEY + "SocialSection", UserHandle.SYSTEM.getIdentifier());
2879         final NotificationAttributes expectedSummaryAttr_social = new NotificationAttributes(
2880                 BASE_FLAGS, mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT,
2881                 NotificationChannel.SOCIAL_MEDIA_ID);
2882         final NotificationChannel newsChannel = new NotificationChannel(
2883                 NotificationChannel.NEWS_ID, NotificationChannel.NEWS_ID,
2884                 IMPORTANCE_LOW);
2885         final String expectedGroupKey_news = GroupHelper.getFullAggregateGroupKey(pkg,
2886                 AGGREGATE_GROUP_KEY + "NewsSection", UserHandle.SYSTEM.getIdentifier());
2887         final NotificationAttributes expectedSummaryAttr_news = new NotificationAttributes(
2888                 BASE_FLAGS, mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT,
2889                 NotificationChannel.NEWS_ID);
2890         for (NotificationRecord record: notificationList) {
2891             if (record.getChannel().getId().equals(channel1.getId())
2892                     && record.getNotification().isGroupChild()
2893                     && record.getSbn().getId() % 2 == 0) {
2894                 record.updateNotificationChannel(socialChannel);
2895                 mGroupHelper.onChannelUpdated(record);
2896             }
2897             if (record.getChannel().getId().equals(channel1.getId())
2898                     && record.getNotification().isGroupChild()
2899                     && record.getSbn().getId() % 2 != 0) {
2900                 record.updateNotificationChannel(newsChannel);
2901                 mGroupHelper.onChannelUpdated(record);
2902             }
2903         }
2904 
2905         // Check that 2 autogroup summaries were created for the news & social sections
2906         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
2907                 eq(expectedGroupKey_social), anyInt(), eq(expectedSummaryAttr_social));
2908         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
2909                 eq(expectedGroupKey_news), anyInt(), eq(expectedSummaryAttr_news));
2910         // Check that half of the child notifications were grouped in each new section
2911         verify(mCallback, times(numChildNotif / 2)).addAutoGroup(anyString(),
2912                 eq(expectedGroupKey_news), eq(true));
2913         verify(mCallback, times(numChildNotif / 2)).addAutoGroup(anyString(),
2914                 eq(expectedGroupKey_social), eq(true));
2915         verify(mCallback, never()).removeAutoGroup(anyString());
2916         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
2917         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
2918                 any());
2919         verify(mCallback, times(numChildNotif)).removeAppProvidedSummaryOnClassification(
2920                 anyString(), eq(originalAppGroupKey));
2921     }
2922 
2923     @Test
2924     @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
2925             FLAG_NOTIFICATION_CLASSIFICATION,
2926             FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION})
testCacheAndCancelAppSummary_notificationBundled()2927     public void testCacheAndCancelAppSummary_notificationBundled() {
2928         // check that the original app summary is canceled & cached on classification regrouping
2929         final List<NotificationRecord> notificationList = new ArrayList<>();
2930         final String pkg = "package";
2931         final int summaryId = 0;
2932         final int numChildNotif = 4;
2933 
2934         // Create an app-provided group: summary + child notifications
2935         final NotificationChannel channel1 = new NotificationChannel("TEST_CHANNEL_ID1",
2936                 "TEST_CHANNEL_ID1", IMPORTANCE_DEFAULT);
2937         NotificationRecord summary = getNotificationRecord(pkg, summaryId,
2938                 String.valueOf(summaryId), UserHandle.SYSTEM, "testGrp " + summaryId,
2939                 true, channel1);
2940         notificationList.add(summary);
2941         final String originalAppGroupKey = summary.getGroupKey();
2942         final String originalAppGroupName = summary.getNotification().getGroup();
2943         for (int i = 0; i < numChildNotif; i++) {
2944             NotificationRecord child = getNotificationRecord(pkg, i + 42, String.valueOf(i + 42),
2945                     UserHandle.SYSTEM, "testGrp " + summaryId, false, channel1);
2946             notificationList.add(child);
2947         }
2948 
2949         // Last regrouped notification will trigger summary cancellation in NMS
2950         when(mCallback.removeAppProvidedSummaryOnClassification(anyString(),
2951                 eq(originalAppGroupKey))).thenReturn(summary);
2952 
2953         // Classify/bundle child notifications
2954         final NotificationChannel socialChannel = new NotificationChannel(
2955                 NotificationChannel.SOCIAL_MEDIA_ID, NotificationChannel.SOCIAL_MEDIA_ID,
2956                 IMPORTANCE_LOW);
2957         for (NotificationRecord record: notificationList) {
2958             if (record.getChannel().getId().equals(channel1.getId())
2959                     && record.getNotification().isGroupChild()) {
2960                 record.updateNotificationChannel(socialChannel);
2961                 mGroupHelper.onChannelUpdated(record);
2962             }
2963         }
2964 
2965         // Check that the original app summary was cached
2966         CachedSummary cachedSummary = mGroupHelper.findCanceledSummary(pkg,
2967                 String.valueOf(summaryId), summaryId, UserHandle.SYSTEM.getIdentifier());
2968         assertThat(cachedSummary.originalGroupKey()).isEqualTo(originalAppGroupName);
2969         assertThat(cachedSummary.key()).isEqualTo(summary.getKey());
2970 
2971         // App cancels the original summary
2972         reset(mCallback);
2973         mGroupHelper.maybeCancelGroupChildrenForCanceledSummary(pkg, String.valueOf(summaryId),
2974                 summaryId, UserHandle.SYSTEM.getIdentifier(), REASON_APP_CANCEL);
2975         // Check that child notifications are removed and cache is cleared
2976         verify(mCallback, times(1)).removeNotificationFromCanceledGroup(
2977                 eq(UserHandle.SYSTEM.getIdentifier()), eq(pkg), eq(originalAppGroupName),
2978                 eq(REASON_APP_CANCEL));
2979         cachedSummary = mGroupHelper.findCanceledSummary(pkg, String.valueOf(summaryId), summaryId,
2980                 UserHandle.SYSTEM.getIdentifier());
2981         assertThat(cachedSummary).isNull();
2982     }
2983 
2984     @Test
2985     @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
2986             FLAG_NOTIFICATION_CLASSIFICATION,
2987             FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION,
2988             FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS})
testSingletonGroupsRegrouped_notificationBundledBeforeDelayTimeout()2989     public void testSingletonGroupsRegrouped_notificationBundledBeforeDelayTimeout() {
2990         // Check that singleton group notifications are regrouped if classification is done
2991         // before onNotificationPostedWithDelay
2992         final List<NotificationRecord> notificationList = new ArrayList<>();
2993         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
2994         final String pkg = "package";
2995         final int numChildNotifications = AUTOGROUP_AT_COUNT;
2996 
2997         // Post singleton groups, above forced group limit
2998         for (int i = 0; i < numChildNotifications; i++) {
2999             NotificationRecord summary = getNotificationRecord(pkg, i,
3000                     String.valueOf(i), UserHandle.SYSTEM, "testGrp " + i, true);
3001             notificationList.add(summary);
3002             NotificationRecord child = getNotificationRecord(pkg, i + 42, String.valueOf(i + 42),
3003                     UserHandle.SYSTEM, "testGrp " + i, false);
3004             notificationList.add(child);
3005             summaryByGroup.put(summary.getGroupKey(), summary);
3006         }
3007 
3008         // Classify/bundle child notifications
3009         final NotificationChannel socialChannel = new NotificationChannel(
3010                 NotificationChannel.SOCIAL_MEDIA_ID, NotificationChannel.SOCIAL_MEDIA_ID,
3011                 IMPORTANCE_LOW);
3012         final String expectedGroupKey_social = GroupHelper.getFullAggregateGroupKey(pkg,
3013                 AGGREGATE_GROUP_KEY + "SocialSection", UserHandle.SYSTEM.getIdentifier());
3014         final NotificationAttributes expectedSummaryAttr_social = new NotificationAttributes(
3015                 BASE_FLAGS, mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT,
3016                 NotificationChannel.SOCIAL_MEDIA_ID);
3017         for (NotificationRecord record: notificationList) {
3018             if (record.getOriginalGroupKey().contains("testGrp")
3019                     && record.getNotification().isGroupChild()) {
3020                 record.updateNotificationChannel(socialChannel);
3021                 mGroupHelper.onChannelUpdated(record);
3022             }
3023         }
3024 
3025         // Check that notifications are forced grouped and app-provided summaries are canceled
3026         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
3027                 eq(expectedGroupKey_social), anyInt(), eq(expectedSummaryAttr_social));
3028         verify(mCallback, times(numChildNotifications)).addAutoGroup(anyString(),
3029                 eq(expectedGroupKey_social), eq(true));
3030         verify(mCallback, never()).removeAutoGroup(anyString());
3031         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
3032         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
3033                 any());
3034         verify(mCallback, times(numChildNotifications)).removeAppProvidedSummaryOnClassification(
3035                 anyString(), anyString());
3036 
3037         // Adjust group key and cancel summaries
3038         for (NotificationRecord record: notificationList) {
3039             if (record.getNotification().isGroupSummary()) {
3040                 record.isCanceled = true;
3041             } else {
3042                 record.setOverrideGroupKey(expectedGroupKey_social);
3043             }
3044         }
3045 
3046         // Check that after onNotificationPostedWithDelay there is no change in the grouping
3047         reset(mCallback);
3048         for (NotificationRecord record: notificationList) {
3049             mGroupHelper.onNotificationPostedWithDelay(record, notificationList, summaryByGroup);
3050         }
3051 
3052         verify(mCallback, never()).addAutoGroupSummary(anyInt(), anyString(), anyString(),
3053                 anyString(), anyInt(), any());
3054         verify(mCallback, never()).addAutoGroup(anyString(), anyString(), anyBoolean());
3055         verify(mCallback, never()).removeAutoGroup(anyString());
3056         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
3057         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
3058                 any());
3059     }
3060 
3061     @Test
3062     @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
3063             FLAG_NOTIFICATION_CLASSIFICATION,
3064             FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION,
3065             FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS})
testSingletonGroupsRegrouped_notificationBundledAfterDelayTimeout()3066     public void testSingletonGroupsRegrouped_notificationBundledAfterDelayTimeout() {
3067         // Check that singleton group notifications are regrouped if classification is done
3068         // after onNotificationPostedWithDelay
3069         final List<NotificationRecord> notificationList = new ArrayList<>();
3070         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
3071         final String pkg = "package";
3072         final String expectedGroupKey_alerting = GroupHelper.getFullAggregateGroupKey(pkg,
3073                 AGGREGATE_GROUP_KEY + "AlertingSection", UserHandle.SYSTEM.getIdentifier());
3074         String expectedTriggeringKey = null;
3075         // Post singleton groups, above forced group limit
3076         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
3077             NotificationRecord summary = getNotificationRecord(pkg, i,
3078                     String.valueOf(i), UserHandle.SYSTEM, "testGrp " + i, true);
3079             notificationList.add(summary);
3080             NotificationRecord child = getNotificationRecord(pkg, i + 42,
3081                     String.valueOf(i + 42), UserHandle.SYSTEM, "testGrp " + i, false);
3082             notificationList.add(child);
3083             if (i == AUTOGROUP_SINGLETONS_AT_COUNT - 1) {
3084                 expectedTriggeringKey = child.getKey();
3085             }
3086             summaryByGroup.put(summary.getGroupKey(), summary);
3087             mGroupHelper.onNotificationPostedWithDelay(child, notificationList, summaryByGroup);
3088             summary.isCanceled = true;  // simulate removing the app summary
3089             mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
3090         }
3091 
3092         // Check that notifications are forced grouped and app-provided summaries are canceled
3093         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg),
3094                 eq(expectedTriggeringKey), eq(expectedGroupKey_alerting), anyInt(),
3095                 eq(getNotificationAttributes(BASE_FLAGS)));
3096         verify(mCallback, never()).removeAutoGroup(anyString());
3097         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
3098         assertThat(mGroupHelper.findCanceledSummary(pkg, String.valueOf(0), 0,
3099                 UserHandle.SYSTEM.getIdentifier())).isNotNull();
3100         assertThat(mGroupHelper.findCanceledSummary(pkg, String.valueOf(1), 1,
3101                 UserHandle.SYSTEM.getIdentifier())).isNotNull();
3102 
3103         // Classify/bundle child notifications
3104         reset(mCallback);
3105         final NotificationChannel socialChannel = new NotificationChannel(
3106                 NotificationChannel.SOCIAL_MEDIA_ID, NotificationChannel.SOCIAL_MEDIA_ID,
3107                 IMPORTANCE_LOW);
3108         final String expectedGroupKey_social = GroupHelper.getFullAggregateGroupKey(pkg,
3109                 AGGREGATE_GROUP_KEY + "SocialSection", UserHandle.SYSTEM.getIdentifier());
3110         final NotificationAttributes expectedSummaryAttr_social = new NotificationAttributes(
3111                 BASE_FLAGS, mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT,
3112                 NotificationChannel.SOCIAL_MEDIA_ID);
3113         for (NotificationRecord record: notificationList) {
3114             if (record.getOriginalGroupKey().contains("testGrp")
3115                     && record.getNotification().isGroupChild()) {
3116                 record.updateNotificationChannel(socialChannel);
3117                 mGroupHelper.onChannelUpdated(record);
3118             }
3119         }
3120 
3121         // Check that all notifications are moved to the social section group
3122         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
3123                 eq(expectedGroupKey_social), anyInt(), eq(expectedSummaryAttr_social));
3124         verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(),
3125                 eq(expectedGroupKey_social), eq(true));
3126         // Check that the alerting section group is removed
3127         verify(mCallback, times(1)).removeAutoGroupSummary(anyInt(), eq(pkg),
3128                 eq(expectedGroupKey_alerting));
3129         verify(mCallback, times(AUTOGROUP_AT_COUNT - 1)).updateAutogroupSummary(anyInt(),
3130                 anyString(), anyString(), any());
3131     }
3132 
3133     @Test
3134     @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
3135             FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION,
3136             FLAG_NOTIFICATION_CLASSIFICATION})
testValidGroupsRegrouped_notificationBundledWhileEnqueued()3137     public void testValidGroupsRegrouped_notificationBundledWhileEnqueued() {
3138         // Check that valid group notifications are regrouped if classification is done
3139         // before onNotificationPostedWithDelay (within DELAY_FOR_ASSISTANT_TIME)
3140         final List<NotificationRecord> notificationList = new ArrayList<>();
3141         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
3142         final String pkg = "package";
3143 
3144         final int summaryId = 0;
3145         final int numChildren = AUTOGROUP_AT_COUNT;
3146         // Post a regular/valid group: summary + notifications
3147         NotificationRecord summary = getNotificationRecord(pkg, summaryId,
3148                 String.valueOf(summaryId), UserHandle.SYSTEM, "testGrp", true);
3149         notificationList.add(summary);
3150         summaryByGroup.put(summary.getGroupKey(), summary);
3151         for (int i = 0; i < numChildren; i++) {
3152             NotificationRecord child = getNotificationRecord(pkg, i + 42, String.valueOf(i + 42),
3153                     UserHandle.SYSTEM, "testGrp", false);
3154             notificationList.add(child);
3155         }
3156 
3157         // Classify/bundle child notifications. Don't call onChannelUpdated,
3158         // adjustments applied while enqueued will use NotificationAdjustmentExtractor.
3159         final NotificationChannel socialChannel = new NotificationChannel(
3160                 NotificationChannel.SOCIAL_MEDIA_ID, NotificationChannel.SOCIAL_MEDIA_ID,
3161                 IMPORTANCE_LOW);
3162         final String expectedGroupKey_social = GroupHelper.getFullAggregateGroupKey(pkg,
3163                 AGGREGATE_GROUP_KEY + "SocialSection", UserHandle.SYSTEM.getIdentifier());
3164         final NotificationAttributes expectedSummaryAttr_social = new NotificationAttributes(
3165                 BASE_FLAGS, mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT,
3166                 NotificationChannel.SOCIAL_MEDIA_ID);
3167         for (NotificationRecord record: notificationList) {
3168             if (record.getOriginalGroupKey().contains("testGrp")
3169                     && record.getNotification().isGroupChild()) {
3170                 record.updateNotificationChannel(socialChannel);
3171             }
3172         }
3173 
3174         // Check that notifications are forced grouped and app-provided summaries are canceled
3175         for (NotificationRecord record: notificationList) {
3176             mGroupHelper.onNotificationPostedWithDelay(record, notificationList, summaryByGroup);
3177         }
3178 
3179         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
3180                 eq(expectedGroupKey_social), anyInt(), eq(expectedSummaryAttr_social));
3181         verify(mCallback, times(numChildren)).addAutoGroup(anyString(), eq(expectedGroupKey_social),
3182                 eq(true));
3183         verify(mCallback, never()).removeAutoGroup(anyString());
3184         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
3185         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
3186                 any());
3187         verify(mCallback, times(numChildren)).removeAppProvidedSummaryOnClassification(anyString(),
3188                 anyString());
3189     }
3190 
3191     @Test
3192     @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
3193             FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION,
3194             FLAG_NOTIFICATION_CLASSIFICATION})
testUnbundleNotification_originalSummaryMissing_autogroupInNewSection()3195     public void testUnbundleNotification_originalSummaryMissing_autogroupInNewSection() {
3196         // Check that unbundled notifications are moved to the original section and aggregated
3197         // with existing autogrouped notifications
3198         final List<NotificationRecord> notificationList = new ArrayList<>();
3199         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
3200         final String pkg = "package";
3201 
3202         final int summaryId = 0;
3203         final int numChildren = AUTOGROUP_AT_COUNT - 1;
3204         // Post a regular/valid group: summary + notifications (one less than autogroup limit)
3205         NotificationRecord summary = getNotificationRecord(pkg, summaryId,
3206                 String.valueOf(summaryId), UserHandle.SYSTEM, "testGrp", true);
3207         notificationList.add(summary);
3208         summaryByGroup.put(summary.getGroupKey(), summary);
3209         final String originalAppGroupKey = summary.getGroupKey();
3210         final NotificationChannel originalChannel = summary.getChannel();
3211         for (int i = 0; i < numChildren; i++) {
3212             NotificationRecord child = getNotificationRecord(pkg, i + 42, String.valueOf(i + 42),
3213                     UserHandle.SYSTEM, "testGrp", false);
3214             notificationList.add(child);
3215             mGroupHelper.onNotificationPostedWithDelay(child, notificationList, summaryByGroup);
3216         }
3217 
3218         // Classify/bundle all child notifications: original group & summary is removed
3219         final NotificationChannel socialChannel = new NotificationChannel(
3220                 NotificationChannel.SOCIAL_MEDIA_ID, NotificationChannel.SOCIAL_MEDIA_ID,
3221                 IMPORTANCE_LOW);
3222         for (NotificationRecord record: notificationList) {
3223             if (record.getOriginalGroupKey().contains("testGrp")
3224                     && record.getNotification().isGroupChild()) {
3225                 record.updateNotificationChannel(socialChannel);
3226                 mGroupHelper.onChannelUpdated(record);
3227             }
3228         }
3229 
3230         // Check that no autogroup summaries were created for the social section
3231         verify(mCallback, never()).addAutoGroupSummary(anyInt(), anyString(), anyString(),
3232                 anyString(), anyInt(), any());
3233         verify(mCallback, never()).addAutoGroup(anyString(), anyString(), anyBoolean());
3234         verify(mCallback, never()).removeAutoGroup(anyString());
3235         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
3236         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
3237                 any());
3238         verify(mCallback, times(numChildren)).removeAppProvidedSummaryOnClassification(
3239                 anyString(), eq(originalAppGroupKey));
3240 
3241         // Cancel summary
3242         summary.isCanceled = true;
3243         summaryByGroup.clear();
3244         notificationList.remove(summary);
3245 
3246         // Add 1 ungrouped notification in the original section
3247         NotificationRecord ungroupedNotification = getNotificationRecord(pkg, 4242,
3248                 String.valueOf(4242), UserHandle.SYSTEM);
3249         notificationList.add(ungroupedNotification);
3250         mGroupHelper.onNotificationPosted(ungroupedNotification, false);
3251 
3252         // Unbundle the bundled notifications => notifications are moved back to the original group
3253         // and an aggregate group is created because autogroup limit is reached
3254         reset(mCallback);
3255         for (NotificationRecord record: notificationList) {
3256             if (record.getNotification().isGroupChild()
3257                     && record.getOriginalGroupKey().contains("testGrp")
3258                     && NotificationChannel.SYSTEM_RESERVED_IDS.contains(
3259                         record.getChannel().getId())) {
3260                 record.updateNotificationChannel(originalChannel);
3261                 mGroupHelper.onNotificationUnbundled(record, false);
3262             }
3263         }
3264 
3265         // Check that a new aggregate group is created
3266         final String expectedGroupKey_alerting = GroupHelper.getFullAggregateGroupKey(pkg,
3267                 AGGREGATE_GROUP_KEY + "AlertingSection", UserHandle.SYSTEM.getIdentifier());
3268         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
3269                 eq(expectedGroupKey_alerting), anyInt(), any());
3270         verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(),
3271                 eq(expectedGroupKey_alerting), eq(true));
3272         verify(mCallback, never()).removeAutoGroup(anyString());
3273         verify(mCallback, times(numChildren)).removeAutoGroupSummary(anyInt(), anyString(),
3274                 anyString());
3275         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
3276                 any());
3277     }
3278 
3279     @Test
3280     @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
3281             FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION,
3282             FLAG_NOTIFICATION_CLASSIFICATION})
testUnbundleNotification_originalSummaryExists()3283     public void testUnbundleNotification_originalSummaryExists() {
3284         // Check that unbundled notifications are moved to the original section and original group
3285         // when the original summary is still present
3286         final List<NotificationRecord> notificationList = new ArrayList<>();
3287         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
3288         final String pkg = "package";
3289 
3290         final int summaryId = 0;
3291         final int numChildren = AUTOGROUP_AT_COUNT + 1;
3292         // Post a regular/valid group: summary + notifications
3293         NotificationRecord summary = getNotificationRecord(pkg, summaryId,
3294                 String.valueOf(summaryId), UserHandle.SYSTEM, "testGrp", true);
3295         notificationList.add(summary);
3296         summaryByGroup.put(summary.getGroupKey(), summary);
3297         final String originalAppGroupKey = summary.getGroupKey();
3298         final NotificationChannel originalChannel = summary.getChannel();
3299         for (int i = 0; i < numChildren; i++) {
3300             NotificationRecord child = getNotificationRecord(pkg, i + 42, String.valueOf(i + 42),
3301                     UserHandle.SYSTEM, "testGrp", false);
3302             notificationList.add(child);
3303             mGroupHelper.onNotificationPostedWithDelay(child, notificationList, summaryByGroup);
3304         }
3305 
3306         // Classify/bundle child notifications: all except one, to keep the original group
3307         final NotificationChannel socialChannel = new NotificationChannel(
3308                 NotificationChannel.SOCIAL_MEDIA_ID, NotificationChannel.SOCIAL_MEDIA_ID,
3309                 IMPORTANCE_LOW);
3310         final String expectedGroupKey_social = GroupHelper.getFullAggregateGroupKey(pkg,
3311                 AGGREGATE_GROUP_KEY + "SocialSection", UserHandle.SYSTEM.getIdentifier());
3312         final NotificationAttributes expectedSummaryAttr_social = new NotificationAttributes(
3313                 BASE_FLAGS, mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT,
3314                 NotificationChannel.SOCIAL_MEDIA_ID);
3315         int numChildrenBundled = 0;
3316         for (NotificationRecord record: notificationList) {
3317             if (record.getOriginalGroupKey().contains("testGrp")
3318                     && record.getNotification().isGroupChild()) {
3319                 record.updateNotificationChannel(socialChannel);
3320                 mGroupHelper.onChannelUpdated(record);
3321                 numChildrenBundled++;
3322                 if (numChildrenBundled == AUTOGROUP_AT_COUNT) {
3323                     break;
3324                 }
3325             }
3326         }
3327 
3328         // Check that 1 autogroup summaries were created for the social section
3329         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
3330                 eq(expectedGroupKey_social), anyInt(), eq(expectedSummaryAttr_social));
3331         verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(),
3332                 eq(expectedGroupKey_social), eq(true));
3333         verify(mCallback, never()).removeAutoGroup(anyString());
3334         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
3335         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
3336                 any());
3337         verify(mCallback, times(AUTOGROUP_AT_COUNT)).removeAppProvidedSummaryOnClassification(
3338                 anyString(), eq(originalAppGroupKey));
3339 
3340         // Adjust group key and cancel summaries
3341         for (NotificationRecord record: notificationList) {
3342             if (record.getNotification().isGroupSummary()) {
3343                 record.isCanceled = true;
3344             } else if (record.getOriginalGroupKey().contains("testGrp")
3345                         && NotificationChannel.SYSTEM_RESERVED_IDS.contains(
3346                         record.getChannel().getId())) {
3347                 record.setOverrideGroupKey(expectedGroupKey_social);
3348             }
3349         }
3350 
3351         // Add 1 ungrouped notification in the original section
3352         NotificationRecord ungroupedNotification = getNotificationRecord(pkg, 4242,
3353                 String.valueOf(4242), UserHandle.SYSTEM);
3354         notificationList.add(ungroupedNotification);
3355         mGroupHelper.onNotificationPosted(ungroupedNotification, false);
3356 
3357         // Unbundle the bundled notifications => social section summary is destroyed
3358         // and notifications are moved back to the original group
3359         reset(mCallback);
3360         for (NotificationRecord record: notificationList) {
3361             if (record.getNotification().isGroupChild()
3362                     && record.getOriginalGroupKey().contains("testGrp")
3363                     && NotificationChannel.SYSTEM_RESERVED_IDS.contains(
3364                         record.getChannel().getId())) {
3365                 record.updateNotificationChannel(originalChannel);
3366                 mGroupHelper.onNotificationUnbundled(record, true);
3367             }
3368         }
3369 
3370         // Check that the autogroup summary for the social section was removed
3371         // and that no new autogroup summaries were created
3372         verify(mCallback, never()).addAutoGroupSummary(anyInt(), anyString(), anyString(),
3373                 anyString(), anyInt(), any());
3374         verify(mCallback, never()).addAutoGroup(anyString(), anyString(), anyBoolean());
3375         verify(mCallback, never()).removeAutoGroup(anyString());
3376         verify(mCallback, times(1)).removeAutoGroupSummary(anyInt(), eq(pkg),
3377                 eq(expectedGroupKey_social));
3378         verify(mCallback, times(AUTOGROUP_AT_COUNT - 1)).updateAutogroupSummary(anyInt(), eq(pkg),
3379                 eq(expectedGroupKey_social), any());
3380 
3381         for (NotificationRecord record: notificationList) {
3382             if (record.getNotification().isGroupChild()
3383                     && record.getOriginalGroupKey().contains("testGrp")) {
3384                 assertThat(record.getSbn().getOverrideGroupKey()).isNull();
3385             }
3386         }
3387     }
3388 
3389     @Test
3390     @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
3391             FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION,
3392             FLAG_NOTIFICATION_CLASSIFICATION})
testUnbundleByImportanceNotification_originalSummaryExists()3393     public void testUnbundleByImportanceNotification_originalSummaryExists() {
3394         // Check that unbundled notifications are moved to the original section and original group
3395         // when the original summary is still present
3396         final List<NotificationRecord> notificationList = new ArrayList<>();
3397         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
3398         final String pkg = "package";
3399 
3400         final int summaryId = 0;
3401         final int numChildren = AUTOGROUP_AT_COUNT + 1;
3402         // Post a regular/valid group: summary + notifications
3403         NotificationRecord summary = getNotificationRecord(pkg, summaryId,
3404                 String.valueOf(summaryId), UserHandle.SYSTEM, "testGrp", true);
3405         notificationList.add(summary);
3406         summaryByGroup.put(summary.getGroupKey(), summary);
3407         final String originalAppGroupKey = summary.getGroupKey();
3408         final NotificationChannel originalChannel = summary.getChannel();
3409         for (int i = 0; i < numChildren; i++) {
3410             NotificationRecord child = getNotificationRecord(pkg, i + 42, String.valueOf(i + 42),
3411                     UserHandle.SYSTEM, "testGrp", false);
3412             notificationList.add(child);
3413             mGroupHelper.onNotificationPostedWithDelay(child, notificationList, summaryByGroup);
3414         }
3415 
3416         // Classify/bundle child notifications: all except one, to keep the original group
3417         final NotificationChannel socialChannel = new NotificationChannel(
3418                 NotificationChannel.SOCIAL_MEDIA_ID, NotificationChannel.SOCIAL_MEDIA_ID,
3419                 IMPORTANCE_LOW);
3420         final String expectedGroupKey_social = GroupHelper.getFullAggregateGroupKey(pkg,
3421                 AGGREGATE_GROUP_KEY + "SocialSection", UserHandle.SYSTEM.getIdentifier());
3422         final NotificationAttributes expectedSummaryAttr_social = new NotificationAttributes(
3423                 BASE_FLAGS, mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT,
3424                 NotificationChannel.SOCIAL_MEDIA_ID);
3425         int numChildrenBundled = 0;
3426         for (NotificationRecord record: notificationList) {
3427             if (record.getOriginalGroupKey().contains("testGrp")
3428                     && record.getNotification().isGroupChild()) {
3429                 record.updateNotificationChannel(socialChannel);
3430                 mGroupHelper.onChannelUpdated(record);
3431                 numChildrenBundled++;
3432                 if (numChildrenBundled == AUTOGROUP_AT_COUNT) {
3433                     break;
3434                 }
3435             }
3436         }
3437 
3438         // Check that 1 autogroup summaries were created for the social section
3439         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
3440                 eq(expectedGroupKey_social), anyInt(), eq(expectedSummaryAttr_social));
3441         verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(),
3442                 eq(expectedGroupKey_social), eq(true));
3443         verify(mCallback, never()).removeAutoGroup(anyString());
3444         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
3445         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
3446                 any());
3447         verify(mCallback, times(AUTOGROUP_AT_COUNT)).removeAppProvidedSummaryOnClassification(
3448                 anyString(), eq(originalAppGroupKey));
3449 
3450         // Adjust group key for grouped notifications
3451         for (NotificationRecord record: notificationList) {
3452             if (record.getOriginalGroupKey().contains("testGrp")
3453                     && NotificationChannel.SYSTEM_RESERVED_IDS.contains(
3454                         record.getChannel().getId())) {
3455                 record.setOverrideGroupKey(expectedGroupKey_social);
3456             }
3457         }
3458 
3459         // Add 1 ungrouped notification in the original section
3460         NotificationRecord ungroupedNotification = getNotificationRecord(pkg, 4242,
3461                 String.valueOf(4242), UserHandle.SYSTEM);
3462         notificationList.add(ungroupedNotification);
3463         mGroupHelper.onNotificationPosted(ungroupedNotification, false);
3464 
3465         // Unbundle the bundled notifications by changing the social channel importance to alerting
3466         // => social section summary is destroyed
3467         // and notifications are moved back to the original group
3468         reset(mCallback);
3469         socialChannel.setImportance(IMPORTANCE_DEFAULT);
3470         for (NotificationRecord record: notificationList) {
3471             if (record.getNotification().isGroupChild()
3472                     && record.getOriginalGroupKey().contains("testGrp")
3473                     && NotificationChannel.SYSTEM_RESERVED_IDS.contains(
3474                         record.getChannel().getId())) {
3475                 record.updateNotificationChannel(socialChannel);
3476             }
3477         }
3478         mGroupHelper.onChannelUpdated(UserHandle.SYSTEM.getIdentifier(), pkg, socialChannel,
3479                 notificationList, summaryByGroup);
3480 
3481         // Check that the autogroup summary for the social section was removed
3482         // and that no new autogroup summaries were created
3483         verify(mCallback, never()).addAutoGroupSummary(anyInt(), anyString(), anyString(),
3484                 anyString(), anyInt(), any());
3485         verify(mCallback, never()).addAutoGroup(anyString(), anyString(), anyBoolean());
3486         verify(mCallback, never()).removeAutoGroup(anyString());
3487         verify(mCallback, times(1)).removeAutoGroupSummary(anyInt(), eq(pkg),
3488                 eq(expectedGroupKey_social));
3489 
3490         for (NotificationRecord record: notificationList) {
3491             if (record.getNotification().isGroupChild()
3492                     && record.getOriginalGroupKey().contains("testGrp")) {
3493                 assertThat(record.getSbn().getOverrideGroupKey()).isNull();
3494                 assertThat(GroupHelper.getSection(record).mName).isEqualTo("AlertingSection");
3495             }
3496         }
3497     }
3498 
3499     @Test
3500     @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
3501             FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION,
3502             FLAG_NOTIFICATION_CLASSIFICATION})
testUnbundleByImportanceNotification_originalSummaryRemoved()3503     public void testUnbundleByImportanceNotification_originalSummaryRemoved() {
3504         // Check that unbundled notifications are moved to the original section and autogrouped
3505         // when the original summary is not present
3506         final List<NotificationRecord> notificationList = new ArrayList<>();
3507         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
3508         final String pkg = "package";
3509 
3510         final int summaryId = 0;
3511         final int numChildren = AUTOGROUP_AT_COUNT + 1;
3512         // Post a regular/valid group: summary + notifications
3513         NotificationRecord summary = getNotificationRecord(pkg, summaryId,
3514                 String.valueOf(summaryId), UserHandle.SYSTEM, "testGrp", true);
3515         notificationList.add(summary);
3516         summaryByGroup.put(summary.getGroupKey(), summary);
3517         final String originalAppGroupKey = summary.getGroupKey();
3518         final NotificationChannel originalChannel = summary.getChannel();
3519         for (int i = 0; i < numChildren; i++) {
3520             NotificationRecord child = getNotificationRecord(pkg, i + 42, String.valueOf(i + 42),
3521                     UserHandle.SYSTEM, "testGrp", false);
3522             notificationList.add(child);
3523             mGroupHelper.onNotificationPostedWithDelay(child, notificationList, summaryByGroup);
3524         }
3525 
3526         // Classify/bundle child notifications: all except one, to keep the original group
3527         final NotificationChannel socialChannel = new NotificationChannel(
3528                 NotificationChannel.SOCIAL_MEDIA_ID, NotificationChannel.SOCIAL_MEDIA_ID,
3529                 IMPORTANCE_LOW);
3530         final String expectedGroupKey_social = GroupHelper.getFullAggregateGroupKey(pkg,
3531                 AGGREGATE_GROUP_KEY + "SocialSection", UserHandle.SYSTEM.getIdentifier());
3532         final NotificationAttributes expectedSummaryAttr_social = new NotificationAttributes(
3533                 BASE_FLAGS, mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT,
3534                 NotificationChannel.SOCIAL_MEDIA_ID);
3535         int numChildrenBundled = 0;
3536         for (NotificationRecord record: notificationList) {
3537             if (record.getOriginalGroupKey().contains("testGrp")
3538                     && record.getNotification().isGroupChild()) {
3539                 record.updateNotificationChannel(socialChannel);
3540                 mGroupHelper.onChannelUpdated(record);
3541                 numChildrenBundled++;
3542                 if (numChildrenBundled == AUTOGROUP_AT_COUNT) {
3543                     break;
3544                 }
3545             }
3546         }
3547 
3548         // Check that 1 autogroup summaries were created for the social section
3549         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
3550                 eq(expectedGroupKey_social), anyInt(), eq(expectedSummaryAttr_social));
3551         verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(),
3552                 eq(expectedGroupKey_social), eq(true));
3553         verify(mCallback, never()).removeAutoGroup(anyString());
3554         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
3555         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
3556                 any());
3557         verify(mCallback, times(AUTOGROUP_AT_COUNT)).removeAppProvidedSummaryOnClassification(
3558                 anyString(), eq(originalAppGroupKey));
3559 
3560         // Adjust group key
3561         for (NotificationRecord record: notificationList) {
3562             if (record.getOriginalGroupKey().contains("testGrp")
3563                     && NotificationChannel.SYSTEM_RESERVED_IDS.contains(
3564                         record.getChannel().getId())) {
3565                 record.setOverrideGroupKey(expectedGroupKey_social);
3566             }
3567         }
3568 
3569         // Remove original summary
3570         notificationList.remove(summary);
3571         summaryByGroup.remove(summary.getGroupKey());
3572 
3573         // Add 1 ungrouped notification in the original section
3574         NotificationRecord ungroupedNotification = getNotificationRecord(pkg, 4242,
3575                 String.valueOf(4242), UserHandle.SYSTEM);
3576         notificationList.add(ungroupedNotification);
3577         mGroupHelper.onNotificationPosted(ungroupedNotification, false);
3578 
3579         // Unbundle the bundled notifications by changing the social channel importance to alerting
3580         // => social section summary is destroyed
3581         // and notifications are moved back to the alerting section and autogrouped
3582         reset(mCallback);
3583         socialChannel.setImportance(IMPORTANCE_DEFAULT);
3584         for (NotificationRecord record: notificationList) {
3585             if (record.getNotification().isGroupChild()
3586                     && record.getOriginalGroupKey().contains("testGrp")
3587                     && NotificationChannel.SYSTEM_RESERVED_IDS.contains(
3588                         record.getChannel().getId())) {
3589                 record.updateNotificationChannel(socialChannel);
3590             }
3591         }
3592         mGroupHelper.onChannelUpdated(UserHandle.SYSTEM.getIdentifier(), pkg, socialChannel,
3593                 notificationList, summaryByGroup);
3594 
3595         // Check that the autogroup summary for the social section was removed
3596         // and that a new autogroup was created in the alerting section
3597         final String expectedGroupKey_alerting = GroupHelper.getFullAggregateGroupKey(pkg,
3598                 AGGREGATE_GROUP_KEY + "AlertingSection", UserHandle.SYSTEM.getIdentifier());
3599         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
3600                 eq(expectedGroupKey_alerting), anyInt(), any());
3601         verify(mCallback, times(AUTOGROUP_AT_COUNT + 1)).addAutoGroup(anyString(),
3602                 eq(expectedGroupKey_alerting), eq(true));
3603         verify(mCallback, never()).removeAutoGroup(anyString());
3604         verify(mCallback, times(1)).removeAutoGroupSummary(anyInt(), eq(pkg),
3605                 eq(expectedGroupKey_social));
3606         verify(mCallback, never()).removeAppProvidedSummaryOnClassification(anyString(),
3607                 anyString());
3608     }
3609 
3610     @Test
3611     @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
3612             FLAG_NOTIFICATION_CLASSIFICATION,
3613             FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION})
testClassifyWithAlertingImportance_doesNotBundle()3614     public void testClassifyWithAlertingImportance_doesNotBundle() {
3615         // Check that classified notifications are autogrouped when channel importance
3616         // is updated DEFAULT to LOW
3617         final List<NotificationRecord> notificationList = new ArrayList<>();
3618         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
3619         final String pkg = "package";
3620 
3621         final int summaryId = 0;
3622         final int numChildren = AUTOGROUP_AT_COUNT + 1;
3623         // Post a regular/valid group: summary + notifications
3624         NotificationRecord summary = getNotificationRecord(pkg, summaryId,
3625                 String.valueOf(summaryId), UserHandle.SYSTEM, "testGrp", true);
3626         notificationList.add(summary);
3627         summaryByGroup.put(summary.getGroupKey(), summary);
3628         final String originalAppGroupKey = summary.getGroupKey();
3629         final NotificationChannel originalChannel = summary.getChannel();
3630         for (int i = 0; i < numChildren; i++) {
3631             NotificationRecord child = getNotificationRecord(pkg, i + 42, String.valueOf(i + 42),
3632                     UserHandle.SYSTEM, "testGrp", false);
3633             notificationList.add(child);
3634             mGroupHelper.onNotificationPostedWithDelay(child, notificationList, summaryByGroup);
3635         }
3636 
3637         // Classify child notifications to Alerting bundle channel => do not "bundle"
3638         final NotificationChannel socialChannel = new NotificationChannel(
3639                 NotificationChannel.SOCIAL_MEDIA_ID, NotificationChannel.SOCIAL_MEDIA_ID,
3640                 IMPORTANCE_DEFAULT);
3641         int numChildrenBundled = 0;
3642         for (NotificationRecord record: notificationList) {
3643             if (record.getOriginalGroupKey().contains("testGrp")
3644                     && record.getNotification().isGroupChild()) {
3645                 record.updateNotificationChannel(socialChannel);
3646                 mGroupHelper.onChannelUpdated(record);
3647                 numChildrenBundled++;
3648                 if (numChildrenBundled == AUTOGROUP_AT_COUNT) {
3649                     break;
3650                 }
3651             }
3652         }
3653 
3654         // Check that no autogroup summaries were created for the social section
3655         verify(mCallback, never()).addAutoGroupSummary(anyInt(), anyString(), anyString(),
3656                 anyString(), anyInt(), any());
3657         verify(mCallback, never()).addAutoGroup(anyString(), anyString(), anyBoolean());
3658         verify(mCallback, never()).removeAutoGroup(anyString());
3659         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
3660         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
3661                 any());
3662         verify(mCallback, never()).removeAppProvidedSummaryOnClassification(anyString(),
3663                 anyString());
3664 
3665         // Change importance to LOW => autogroup notifications in bundle section
3666         reset(mCallback);
3667         final String expectedGroupKey_social = GroupHelper.getFullAggregateGroupKey(pkg,
3668                 AGGREGATE_GROUP_KEY + "SocialSection", UserHandle.SYSTEM.getIdentifier());
3669         final NotificationAttributes expectedSummaryAttr_social = new NotificationAttributes(
3670                 BASE_FLAGS, mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT,
3671                 NotificationChannel.SOCIAL_MEDIA_ID);
3672         socialChannel.setImportance(IMPORTANCE_LOW);
3673         for (NotificationRecord record: notificationList) {
3674             if (record.getNotification().isGroupChild()
3675                     && record.getOriginalGroupKey().contains("testGrp")
3676                     && NotificationChannel.SYSTEM_RESERVED_IDS.contains(
3677                     record.getChannel().getId())) {
3678                 record.updateNotificationChannel(socialChannel);
3679             }
3680         }
3681         mGroupHelper.onChannelUpdated(UserHandle.SYSTEM.getIdentifier(), pkg, socialChannel,
3682                 notificationList, summaryByGroup);
3683 
3684         // Check that 1 autogroup summaries were created for the social section
3685         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
3686                 eq(expectedGroupKey_social), anyInt(), eq(expectedSummaryAttr_social));
3687         verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(),
3688                 eq(expectedGroupKey_social), eq(true));
3689         verify(mCallback, never()).removeAutoGroup(anyString());
3690         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
3691         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
3692                 any());
3693         verify(mCallback, times(AUTOGROUP_AT_COUNT)).removeAppProvidedSummaryOnClassification(
3694                 anyString(), eq(originalAppGroupKey));
3695     }
3696 
3697     @Test
3698     @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
testMoveAggregateGroups_updateChannel_groupsUngrouped()3699     public void testMoveAggregateGroups_updateChannel_groupsUngrouped() {
3700         final String pkg = "package";
3701         final String expectedGroupKey_silent = GroupHelper.getFullAggregateGroupKey(pkg,
3702             AGGREGATE_GROUP_KEY + "SilentSection", UserHandle.SYSTEM.getIdentifier());
3703         final List<NotificationRecord> notificationList = new ArrayList<>();
3704         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
3705 
3706         // Post too few group notifications without summaries => do not autogroup
3707         final NotificationChannel lowPrioChannel = new NotificationChannel("TEST_CHANNEL_LOW_ID",
3708                 "TEST_CHANNEL_LOW_ID", IMPORTANCE_LOW);
3709         final int numUngrouped = AUTOGROUP_AT_COUNT - 1;
3710         int startIdx = 42;
3711         for (int i = startIdx; i < startIdx + numUngrouped; i++) {
3712             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
3713                     UserHandle.SYSTEM, "testGrp " + i, false, lowPrioChannel);
3714             notificationList.add(r);
3715             mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
3716         }
3717         verify(mCallback, never()).addAutoGroup(anyString(), anyString(), anyBoolean());
3718         verify(mCallback, never()).addAutoGroupSummary(anyInt(), anyString(), anyString(),
3719                 anyString(), anyInt(), any());
3720 
3721         reset(mCallback);
3722 
3723         final String expectedGroupKey_alerting = GroupHelper.getFullAggregateGroupKey(pkg,
3724             AGGREGATE_GROUP_KEY + "AlertingSection", UserHandle.SYSTEM.getIdentifier());
3725         final NotificationChannel channel = new NotificationChannel(TEST_CHANNEL_ID,
3726                 TEST_CHANNEL_ID, IMPORTANCE_DEFAULT);
3727 
3728         // Post group notifications without summaries => force autogroup
3729         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
3730             NotificationRecord r = getNotificationRecord(pkg, i, String.valueOf(i),
3731                     UserHandle.SYSTEM, "testGrp " + i, false, channel);
3732             notificationList.add(r);
3733             mGroupHelper.onNotificationPostedWithDelay(r, notificationList, summaryByGroup);
3734         }
3735         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
3736                 eq(expectedGroupKey_alerting), anyInt(), eq(getNotificationAttributes(BASE_FLAGS)));
3737         verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(),
3738                 eq(expectedGroupKey_alerting), eq(true));
3739         verify(mCallback, never()).removeAutoGroup(anyString());
3740         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
3741         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
3742                 any());
3743         Mockito.reset(mCallback);
3744 
3745         // Update the channel importance for all posted notifications
3746         final int numSilentGroupNotifications = AUTOGROUP_AT_COUNT + numUngrouped;
3747         channel.setImportance(IMPORTANCE_LOW);
3748         for (NotificationRecord r: notificationList) {
3749             r.updateNotificationChannel(channel);
3750         }
3751         mGroupHelper.onChannelUpdated(UserHandle.SYSTEM.getIdentifier(), pkg, channel,
3752                 notificationList, summaryByGroup);
3753 
3754         // Check that all notifications are moved to the silent section group
3755         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
3756                 eq(expectedGroupKey_silent), anyInt(), eq(getNotificationAttributes(BASE_FLAGS)));
3757         verify(mCallback, times(numSilentGroupNotifications)).addAutoGroup(anyString(),
3758                 eq(expectedGroupKey_silent), eq(true));
3759 
3760         // Check that the alerting section group is removed
3761         verify(mCallback, times(1)).removeAutoGroupSummary(anyInt(), eq(pkg),
3762                 eq(expectedGroupKey_alerting));
3763         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
3764                 any());
3765     }
3766 
3767     @Test
3768     @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
testAutogroup_updateChannel_reachedMinAutogroupCount()3769     public void testAutogroup_updateChannel_reachedMinAutogroupCount() {
3770         final String pkg = "package";
3771         final NotificationChannel channel1 = new NotificationChannel("TEST_CHANNEL_ID1",
3772                 "TEST_CHANNEL_ID1", IMPORTANCE_DEFAULT);
3773         final NotificationChannel channel2 = new NotificationChannel("TEST_CHANNEL_ID2",
3774                 "TEST_CHANNEL_ID2", IMPORTANCE_LOW);
3775         final List<NotificationRecord> notificationList = new ArrayList<>();
3776         // Post notifications with different channels that would autogroup in different sections
3777         NotificationRecord r;
3778         // Not enough notifications to autogroup initially
3779         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
3780             if (i % 2 == 0) {
3781                 r = getNotificationRecord(pkg, i, String.valueOf(i),
3782                     UserHandle.SYSTEM, null, false, channel1);
3783             } else {
3784                 r = getNotificationRecord(pkg, i, String.valueOf(i),
3785                     UserHandle.SYSTEM, null, false, channel2);
3786             }
3787             notificationList.add(r);
3788             mGroupHelper.onNotificationPosted(r, false);
3789         }
3790         verify(mCallback, never()).addAutoGroupSummary(anyInt(), anyString(), anyString(),
3791                 anyString(), anyInt(), any());
3792         verify(mCallback, never()).addAutoGroup(anyString(), anyString(), anyBoolean());
3793         verify(mCallback, never()).removeAutoGroup(anyString());
3794         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
3795         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
3796                 any());
3797         Mockito.reset(mCallback);
3798 
3799         // Update channel1's importance
3800         final String expectedGroupKey_silent = GroupHelper.getFullAggregateGroupKey(pkg,
3801                 AGGREGATE_GROUP_KEY + "SilentSection", UserHandle.SYSTEM.getIdentifier());
3802         channel1.setImportance(IMPORTANCE_LOW);
3803         for (NotificationRecord record: notificationList) {
3804             if (record.getChannel().getId().equals(channel1.getId())) {
3805                 record.updateNotificationChannel(channel1);
3806             }
3807         }
3808         mGroupHelper.onChannelUpdated(UserHandle.SYSTEM.getIdentifier(), pkg, channel1,
3809                 notificationList, new ArrayMap<>());
3810 
3811         // Check that channel1's notifications are moved to the silent section & autogroup all
3812         NotificationAttributes expectedSummaryAttr = new NotificationAttributes(BASE_FLAGS,
3813                 mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT,
3814                 "TEST_CHANNEL_ID1");
3815         verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(),
3816                 eq(expectedGroupKey_silent), eq(true));
3817         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
3818                 eq(expectedGroupKey_silent), anyInt(), eq(expectedSummaryAttr));
3819     }
3820 
3821     @Test
3822     @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
3823             Flags.FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS})
testNoGroup_singletonGroup_underLimit()3824     public void testNoGroup_singletonGroup_underLimit() {
3825         final List<NotificationRecord> notificationList = new ArrayList<>();
3826         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
3827         final String pkg = "package";
3828         // Post singleton groups, under forced group limit
3829         for (int i = 0; i < AUTOGROUP_SINGLETONS_AT_COUNT - 1; i++) {
3830             NotificationRecord summary = getNotificationRecord(pkg, i,
3831                     String.valueOf(i), UserHandle.SYSTEM, "testGrp "+i, true);
3832             notificationList.add(summary);
3833             NotificationRecord child = getNotificationRecord(pkg, i + 42,
3834                     String.valueOf(i + 42), UserHandle.SYSTEM, "testGrp "+i, false);
3835             notificationList.add(child);
3836             summaryByGroup.put(summary.getGroupKey(), summary);
3837             mGroupHelper.onNotificationPostedWithDelay(child, notificationList, summaryByGroup);
3838             mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
3839         }
3840         verifyNoMoreInteractions(mCallback);
3841     }
3842 
3843 
3844     @Test
3845     @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
3846     @DisableFlags(Flags.FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS)
testAddAggregateSummary_singletonGroup_disableFlag()3847     public void testAddAggregateSummary_singletonGroup_disableFlag() {
3848         final List<NotificationRecord> notificationList = new ArrayList<>();
3849         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
3850         final String pkg = "package";
3851         // Post singleton groups, above forced group limit
3852         for (int i = 0; i < AUTOGROUP_SINGLETONS_AT_COUNT; i++) {
3853             NotificationRecord summary = getNotificationRecord(pkg, i,
3854                     String.valueOf(i), UserHandle.SYSTEM, "testGrp "+i, true);
3855             notificationList.add(summary);
3856             NotificationRecord child = getNotificationRecord(pkg, i + 42,
3857                     String.valueOf(i + 42), UserHandle.SYSTEM, "testGrp "+i, false);
3858             notificationList.add(child);
3859             summaryByGroup.put(summary.getGroupKey(), summary);
3860             mGroupHelper.onNotificationPostedWithDelay(child, notificationList, summaryByGroup);
3861             mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
3862         }
3863         // FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS is disabled => don't force group
3864         verifyNoMoreInteractions(mCallback);
3865     }
3866 
3867     @Test
3868     @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
3869             Flags.FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS})
testAddAggregateSummary_singletonGroups()3870     public void testAddAggregateSummary_singletonGroups() {
3871         final List<NotificationRecord> notificationList = new ArrayList<>();
3872         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
3873         final String pkg = "package";
3874         final String expectedGroupKey = GroupHelper.getFullAggregateGroupKey(pkg,
3875             AGGREGATE_GROUP_KEY + "AlertingSection", UserHandle.SYSTEM.getIdentifier());
3876         String expectedTriggeringKey = null;
3877         // Post singleton groups, above forced group limit
3878         for (int i = 0; i < AUTOGROUP_SINGLETONS_AT_COUNT; i++) {
3879             NotificationRecord summary = getNotificationRecord(pkg, i,
3880                 String.valueOf(i), UserHandle.SYSTEM, "testGrp "+i, true);
3881             notificationList.add(summary);
3882             NotificationRecord child = getNotificationRecord(pkg, i + 42,
3883                 String.valueOf(i + 42), UserHandle.SYSTEM, "testGrp "+i, false);
3884             notificationList.add(child);
3885             expectedTriggeringKey = child.getKey();
3886             summaryByGroup.put(summary.getGroupKey(), summary);
3887             mGroupHelper.onNotificationPostedWithDelay(child, notificationList, summaryByGroup);
3888             summary.isCanceled = true;  // simulate removing the app summary
3889             mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
3890         }
3891         // Check that notifications are forced grouped
3892         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg),
3893                 eq(expectedTriggeringKey), eq(expectedGroupKey), anyInt(),
3894                 eq(getNotificationAttributes(BASE_FLAGS)));
3895         verify(mCallback, times(AUTOGROUP_SINGLETONS_AT_COUNT)).addAutoGroup(anyString(),
3896                 eq(expectedGroupKey), eq(true));
3897         verify(mCallback, never()).removeAutoGroup(anyString());
3898         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
3899         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
3900                 any());
3901 
3902         // Check that summaries are canceled
3903         verify(mCallback, times(AUTOGROUP_SINGLETONS_AT_COUNT)).removeAppProvidedSummary(
3904                 anyString());
3905     }
3906 
3907     @Test
3908     @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
3909             Flags.FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS})
testAddAggregateSummary_summaryTriggers_singletonGroups()3910     public void testAddAggregateSummary_summaryTriggers_singletonGroups() {
3911         final List<NotificationRecord> notificationList = new ArrayList<>();
3912         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
3913         final String pkg = "package";
3914         final String expectedGroupKey = GroupHelper.getFullAggregateGroupKey(pkg,
3915                 AGGREGATE_GROUP_KEY + "AlertingSection", UserHandle.SYSTEM.getIdentifier());
3916         final int firstChildIdx = 1;
3917         // Post singleton groups, below forced group limit
3918         for (int i = 0; i < AUTOGROUP_SINGLETONS_AT_COUNT - 1; i++) {
3919             NotificationRecord summary = getNotificationRecord(pkg, i,
3920                     String.valueOf(i), UserHandle.SYSTEM, "testGrp " + i, true);
3921             notificationList.add(summary);
3922             NotificationRecord child = getNotificationRecord(pkg, i + 42,
3923                     String.valueOf(i + 42), UserHandle.SYSTEM, "testGrp " + i, false);
3924             notificationList.add(child);
3925             summaryByGroup.put(summary.getGroupKey(), summary);
3926             mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
3927             mGroupHelper.onNotificationPostedWithDelay(child, notificationList, summaryByGroup);
3928         }
3929 
3930         // Post triggering group summary
3931         final String expectedTriggeringKey = notificationList.get(firstChildIdx).getKey();
3932         final int triggerIdx = AUTOGROUP_SINGLETONS_AT_COUNT - 1;
3933         NotificationRecord summary = getNotificationRecord(pkg, triggerIdx,
3934                 String.valueOf(triggerIdx), UserHandle.SYSTEM, "testGrp " + triggerIdx, true);
3935         notificationList.add(summary);
3936         NotificationRecord child = getNotificationRecord(pkg, triggerIdx + 42,
3937                 String.valueOf(triggerIdx + 42), UserHandle.SYSTEM, "testGrp " + triggerIdx, false);
3938         notificationList.add(child);
3939         summaryByGroup.put(summary.getGroupKey(), summary);
3940         mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
3941 
3942         // Check that notifications are forced grouped
3943         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg),
3944                 eq(expectedTriggeringKey), eq(expectedGroupKey), anyInt(),
3945                 eq(getNotificationAttributes(BASE_FLAGS)));
3946         verify(mCallback, times(AUTOGROUP_SINGLETONS_AT_COUNT)).addAutoGroup(anyString(),
3947                 eq(expectedGroupKey), eq(true));
3948         verify(mCallback, never()).removeAutoGroup(anyString());
3949         verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
3950         verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
3951                 any());
3952 
3953         // Check that summaries are canceled
3954         verify(mCallback, times(AUTOGROUP_SINGLETONS_AT_COUNT)).removeAppProvidedSummary(anyString());
3955     }
3956 
3957     @Test
3958     @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
3959             Flags.FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS})
testCancelCachedSummary_singletonGroups()3960     public void testCancelCachedSummary_singletonGroups() {
3961         final List<NotificationRecord> notificationList = new ArrayList<>();
3962         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
3963         final String pkg = "package";
3964         final int id = 0;
3965         // Post singleton groups, above forced group limit
3966         for (int i = 0; i < AUTOGROUP_SINGLETONS_AT_COUNT; i++) {
3967             NotificationRecord summary = getNotificationRecord(pkg, i,
3968                     String.valueOf(i), UserHandle.SYSTEM, "testGrp "+i, true);
3969             notificationList.add(summary);
3970             NotificationRecord child = getNotificationRecord(pkg, i + 42,
3971                     String.valueOf(i + 42), UserHandle.SYSTEM, "testGrp "+i, false);
3972             notificationList.add(child);
3973             summaryByGroup.put(summary.getGroupKey(), summary);
3974             mGroupHelper.onNotificationPostedWithDelay(child, notificationList, summaryByGroup);
3975             summary.isCanceled = true;  // simulate removing the app summary
3976             mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
3977         }
3978         Mockito.reset(mCallback);
3979 
3980         // App cancels the summary of an aggregated group
3981         mGroupHelper.maybeCancelGroupChildrenForCanceledSummary(pkg, String.valueOf(id), id,
3982                 UserHandle.SYSTEM.getIdentifier(), REASON_APP_CANCEL);
3983 
3984         verify(mCallback, times(1)).removeNotificationFromCanceledGroup(
3985                 eq(UserHandle.SYSTEM.getIdentifier()), eq(pkg), eq("testGrp " + id),
3986                 eq(REASON_APP_CANCEL));
3987         CachedSummary cachedSummary = mGroupHelper.findCanceledSummary(pkg, String.valueOf(id), id,
3988                 UserHandle.SYSTEM.getIdentifier());
3989         assertThat(cachedSummary).isNull();
3990     }
3991 
3992     @Test
3993     @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
3994             Flags.FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS})
testRemoveCachedSummary_singletonGroups_removeChildren()3995     public void testRemoveCachedSummary_singletonGroups_removeChildren() {
3996         final List<NotificationRecord> notificationList = new ArrayList<>();
3997         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
3998         final String pkg = "package";
3999         final String expectedGroupKey = GroupHelper.getFullAggregateGroupKey(pkg,
4000             AGGREGATE_GROUP_KEY + "AlertingSection", UserHandle.SYSTEM.getIdentifier());
4001         final int id = 0;
4002         NotificationRecord childToRemove = null;
4003         // Post singleton groups, above forced group limit
4004         for (int i = 0; i < AUTOGROUP_SINGLETONS_AT_COUNT; i++) {
4005             NotificationRecord summary = getNotificationRecord(pkg, i,
4006                     String.valueOf(i), UserHandle.SYSTEM, "testGrp "+i, true);
4007             notificationList.add(summary);
4008             NotificationRecord child = getNotificationRecord(pkg, i + 42, String.valueOf(i + 42),
4009                     UserHandle.SYSTEM, "testGrp " + i, false);
4010             if (i == id) {
4011                 childToRemove = child;
4012             }
4013             notificationList.add(child);
4014             summaryByGroup.put(summary.getGroupKey(), summary);
4015             mGroupHelper.onNotificationPostedWithDelay(child, notificationList, summaryByGroup);
4016             summary.isCanceled = true;  // simulate removing the app summary
4017             mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
4018         }
4019         // override group key for child notifications
4020         List<NotificationRecord> notificationListAfterGrouping = new ArrayList<>(
4021             notificationList.stream().filter(r -> {
4022                 if (r.getSbn().getNotification().isGroupChild()) {
4023                     r.setOverrideGroupKey(expectedGroupKey);
4024                     return true;
4025                 } else {
4026                     return false;
4027                 }
4028             }).toList());
4029         summaryByGroup.clear();
4030         Mockito.reset(mCallback);
4031 
4032         //Cancel child 0 => remove cached summary
4033         childToRemove.isCanceled = true;
4034         notificationListAfterGrouping.remove(childToRemove);
4035         mGroupHelper.onNotificationRemoved(childToRemove, notificationListAfterGrouping, false);
4036         CachedSummary cachedSummary = mGroupHelper.findCanceledSummary(pkg, String.valueOf(id), id,
4037                 UserHandle.SYSTEM.getIdentifier());
4038         assertThat(cachedSummary).isNull();
4039     }
4040 
4041     @Test
4042     @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
4043     @DisableFlags(FLAG_NOTIFICATION_FORCE_GROUP_CONVERSATIONS)
testNonGroupableChildren_singletonGroups_disableConversations()4044     public void testNonGroupableChildren_singletonGroups_disableConversations() {
4045         // Check that singleton groups with children that are not groupable, is not grouped
4046         // Even though the group summary is a regular (alerting) notification, the children are
4047         // conversations => the group should not be forced grouped.
4048         final List<NotificationRecord> notificationList = new ArrayList<>();
4049         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
4050         final String pkg = "package";
4051 
4052         // Trigger notification, ungrouped
4053         final int triggerId = 1;
4054         NotificationRecord triggerNotification = getNotificationRecord(pkg, triggerId,
4055                 String.valueOf(triggerId), UserHandle.SYSTEM);
4056         notificationList.add(triggerNotification);
4057         final NotificationSectioner triggerSection = GroupHelper.getSection(triggerNotification);
4058         final FullyQualifiedGroupKey triggerFullAggregateGroupKey = new FullyQualifiedGroupKey(
4059                 triggerNotification.getUserId(), triggerNotification.getSbn().getPackageName(),
4060                 triggerSection);
4061 
4062         // Add singleton group with alerting child
4063         final String groupName_valid = "testGrp_valid";
4064         final int summaryId_valid = 0;
4065         NotificationRecord summary = getNotificationRecord(pkg, summaryId_valid,
4066                 String.valueOf(summaryId_valid), UserHandle.SYSTEM, groupName_valid, true);
4067         notificationList.add(summary);
4068         summaryByGroup.put(summary.getGroupKey(), summary);
4069         final String groupKey_valid = summary.getGroupKey();
4070         NotificationRecord child = getNotificationRecord(pkg, summaryId_valid + 42,
4071                 String.valueOf(summaryId_valid + 42), UserHandle.SYSTEM, groupName_valid, false);
4072         notificationList.add(child);
4073 
4074         // Add singleton group with conversation child
4075         final String groupName_invalid = "testGrp_invalid";
4076         final int summaryId_invalid = 100;
4077         summary = getNotificationRecord(pkg, summaryId_invalid,
4078                 String.valueOf(summaryId_invalid), UserHandle.SYSTEM, groupName_invalid, true);
4079         notificationList.add(summary);
4080         final String groupKey_invalid = summary.getGroupKey();
4081         summaryByGroup.put(summary.getGroupKey(), summary);
4082         child = getNotificationRecord(pkg, summaryId_invalid + 42,
4083                 String.valueOf(summaryId_invalid + 42), UserHandle.SYSTEM, groupName_invalid,
4084                 false);
4085         child = spy(child);
4086         when(child.isConversation()).thenReturn(true);
4087         notificationList.add(child);
4088 
4089         // Check that the invalid group will not be force grouped
4090         final ArrayMap<String, NotificationRecord> sparseGroups = mGroupHelper.getSparseGroups(
4091                 triggerFullAggregateGroupKey, notificationList, summaryByGroup, triggerSection);
4092         assertThat(sparseGroups).containsKey(groupKey_valid);
4093         assertThat(sparseGroups).doesNotContainKey(groupKey_invalid);
4094     }
4095 
4096     @Test
4097     @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_FORCE_GROUP_CONVERSATIONS})
testNonGroupableChildren_singletonGroups_enableConversations()4098     public void testNonGroupableChildren_singletonGroups_enableConversations() {
4099         // Check that singleton groups with children that are not groupable, is not grouped
4100         // Conversations are groupable (FLAG_NOTIFICATION_FORCE_GROUP_CONVERSATIONS is enabled)
4101         // The invalid group is the alerting notifications: because the triggering notifications'
4102         // section is Conversations, so the alerting group should be skipped.
4103         final List<NotificationRecord> notificationList = new ArrayList<>();
4104         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
4105         final String pkg = "package";
4106 
4107         // Trigger notification, ungrouped conversation
4108         final int triggerId = 1;
4109         NotificationRecord triggerNotification = getNotificationRecord(pkg, triggerId,
4110                 String.valueOf(triggerId), UserHandle.SYSTEM);
4111         triggerNotification = spy(triggerNotification);
4112         when(triggerNotification.isConversation()).thenReturn(true);
4113         notificationList.add(triggerNotification);
4114         final NotificationSectioner triggerSection = GroupHelper.getSection(triggerNotification);
4115         final FullyQualifiedGroupKey triggerFullAggregateGroupKey = new FullyQualifiedGroupKey(
4116                 triggerNotification.getUserId(), triggerNotification.getSbn().getPackageName(),
4117                 triggerSection);
4118 
4119         // Add singleton group with conversation child
4120         final String groupName_valid = "testGrp_valid";
4121         final int summaryId_valid = 0;
4122         NotificationRecord summary = getNotificationRecord(pkg, summaryId_valid,
4123                 String.valueOf(summaryId_valid), UserHandle.SYSTEM, groupName_valid, true);
4124         summary = spy(summary);
4125         when(summary.isConversation()).thenReturn(true);
4126         notificationList.add(summary);
4127         summaryByGroup.put(summary.getGroupKey(), summary);
4128         final String groupKey_valid = summary.getGroupKey();
4129         NotificationRecord child = getNotificationRecord(pkg, summaryId_valid + 42,
4130                 String.valueOf(summaryId_valid + 42), UserHandle.SYSTEM, groupName_valid, false);
4131         child = spy(child);
4132         when(child.isConversation()).thenReturn(true);
4133         notificationList.add(child);
4134 
4135         // Add singleton group with non-conversation child
4136         final String groupName_invalid = "testGrp_invalid";
4137         final int summaryId_invalid = 100;
4138         summary = getNotificationRecord(pkg, summaryId_invalid,
4139                 String.valueOf(summaryId_invalid), UserHandle.SYSTEM, groupName_invalid, true);
4140         notificationList.add(summary);
4141         final String groupKey_invalid = summary.getGroupKey();
4142         summaryByGroup.put(summary.getGroupKey(), summary);
4143         child = getNotificationRecord(pkg, summaryId_invalid + 42,
4144                 String.valueOf(summaryId_invalid + 42), UserHandle.SYSTEM, groupName_invalid,
4145                 false);
4146         notificationList.add(child);
4147 
4148         // Check that the invalid group will not be force grouped
4149         final ArrayMap<String, NotificationRecord> sparseGroups = mGroupHelper.getSparseGroups(
4150                 triggerFullAggregateGroupKey, notificationList, summaryByGroup, triggerSection);
4151         assertThat(sparseGroups).containsKey(groupKey_valid);
4152         assertThat(sparseGroups).doesNotContainKey(groupKey_invalid);
4153     }
4154 
4155     @Test
4156     @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
4157     @DisableFlags(FLAG_NOTIFICATION_FORCE_GROUP_CONVERSATIONS)
testNonGroupableNotifications()4158     public void testNonGroupableNotifications() {
4159         // Check that there is no valid section for: conversations, calls, foreground services
4160         NotificationRecord notification_conversation = mock(NotificationRecord.class);
4161         when(notification_conversation.isConversation()).thenReturn(true);
4162         assertThat(GroupHelper.getSection(notification_conversation)).isNull();
4163 
4164         NotificationRecord notification_call = spy(getNotificationRecord(mPkg, 0, "", mUser,
4165                 "", false, IMPORTANCE_LOW));
4166         Notification n = mock(Notification.class);
4167         StatusBarNotification sbn = spy(getSbn("package", 0, "0", UserHandle.SYSTEM));
4168         when(notification_call.isConversation()).thenReturn(false);
4169         when(notification_call.getNotification()).thenReturn(n);
4170         when(notification_call.getSbn()).thenReturn(sbn);
4171         when(sbn.getNotification()).thenReturn(n);
4172         when(n.isStyle(Notification.CallStyle.class)).thenReturn(true);
4173         assertThat(GroupHelper.getSection(notification_call)).isNull();
4174 
4175         NotificationRecord notification_colorFg = spy(getNotificationRecord(mPkg, 0, "", mUser,
4176                 "", false, IMPORTANCE_LOW));
4177         sbn = spy(getSbn("package", 0, "0", UserHandle.SYSTEM));
4178         n = mock(Notification.class);
4179         when(notification_colorFg.isConversation()).thenReturn(false);
4180         when(notification_colorFg.getNotification()).thenReturn(n);
4181         when(notification_colorFg.getSbn()).thenReturn(sbn);
4182         when(sbn.getNotification()).thenReturn(n);
4183         when(n.isForegroundService()).thenReturn(true);
4184         when(n.isColorized()).thenReturn(true);
4185         when(n.isStyle(Notification.CallStyle.class)).thenReturn(false);
4186         assertThat(GroupHelper.getSection(notification_colorFg)).isNull();
4187 
4188         NotificationRecord notification_media = spy(getNotificationRecord(mPkg, 0, "", mUser,
4189                 "", false, IMPORTANCE_LOW));
4190         n = mock(Notification.class);
4191         sbn = spy(getSbn("package", 0, "0", UserHandle.SYSTEM));
4192         when(notification_media.isConversation()).thenReturn(false);
4193         when(notification_media.getNotification()).thenReturn(n);
4194         when(notification_media.getSbn()).thenReturn(sbn);
4195         when(sbn.getNotification()).thenReturn(n);
4196         when(n.isMediaNotification()).thenReturn(true);
4197         assertThat(GroupHelper.getSection(notification_media)).isNull();
4198     }
4199 
4200     @Test
4201     @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
4202     @DisableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
testGroupSectioners()4203     public void testGroupSectioners() {
4204         final NotificationRecord notification_alerting = getNotificationRecord(mPkg, 0, "", mUser,
4205                 "", false, IMPORTANCE_DEFAULT);
4206         assertThat(GroupHelper.getSection(notification_alerting).mName).isEqualTo(
4207                 "AlertingSection");
4208 
4209         final NotificationRecord notification_silent = getNotificationRecord(mPkg, 0, "", mUser,
4210                 "", false, IMPORTANCE_LOW);
4211         assertThat(GroupHelper.getSection(notification_silent).mName).isEqualTo("SilentSection");
4212 
4213         // Check that special categories are grouped by their importance
4214         final NotificationChannel promoChannel = new NotificationChannel(
4215                 NotificationChannel.PROMOTIONS_ID, NotificationChannel.PROMOTIONS_ID,
4216                 IMPORTANCE_DEFAULT);
4217         final NotificationRecord notification_promotion = getNotificationRecord(mPkg, 0, "", mUser,
4218                 "", false, promoChannel);
4219         assertThat(GroupHelper.getSection(notification_promotion).mName).isEqualTo(
4220                 "AlertingSection");
4221 
4222         final NotificationChannel newsChannel = new NotificationChannel(NotificationChannel.NEWS_ID,
4223                 NotificationChannel.NEWS_ID, IMPORTANCE_DEFAULT);
4224         final NotificationRecord notification_news = getNotificationRecord(mPkg, 0, "", mUser,
4225                 "", false, newsChannel);
4226         assertThat(GroupHelper.getSection(notification_news).mName).isEqualTo(
4227                 "AlertingSection");
4228 
4229         final NotificationChannel socialChannel = new NotificationChannel(
4230                 NotificationChannel.SOCIAL_MEDIA_ID, NotificationChannel.SOCIAL_MEDIA_ID,
4231                 IMPORTANCE_DEFAULT);
4232         final NotificationRecord notification_social = getNotificationRecord(mPkg, 0, "", mUser,
4233                 "", false, socialChannel);
4234         assertThat(GroupHelper.getSection(notification_social).mName).isEqualTo(
4235                 "AlertingSection");
4236 
4237         final NotificationChannel recsChannel = new NotificationChannel(NotificationChannel.RECS_ID,
4238                 NotificationChannel.RECS_ID, IMPORTANCE_DEFAULT);
4239         final NotificationRecord notification_recs = getNotificationRecord(mPkg, 0, "", mUser,
4240                 "", false, recsChannel);
4241         assertThat(GroupHelper.getSection(notification_recs).mName).isEqualTo(
4242                 "AlertingSection");
4243     }
4244 
4245     @Test
4246     @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_CLASSIFICATION})
testGroupSectioners_withClassificationSections()4247     public void testGroupSectioners_withClassificationSections() {
4248         final NotificationRecord notification_alerting = getNotificationRecord(mPkg, 0, "", mUser,
4249                 "", false, IMPORTANCE_DEFAULT);
4250         assertThat(GroupHelper.getSection(notification_alerting).mName).isEqualTo(
4251                 "AlertingSection");
4252 
4253         final NotificationRecord notification_silent = getNotificationRecord(mPkg, 0, "", mUser,
4254                 "", false, IMPORTANCE_LOW);
4255         assertThat(GroupHelper.getSection(notification_silent).mName).isEqualTo("SilentSection");
4256 
4257         // Check that special categories are grouped in their own sections
4258         final NotificationChannel promoChannel = new NotificationChannel(
4259                 NotificationChannel.PROMOTIONS_ID, NotificationChannel.PROMOTIONS_ID,
4260                 IMPORTANCE_LOW);
4261         final NotificationRecord notification_promotion = getNotificationRecord(mPkg, 0, "", mUser,
4262                 "", false, promoChannel);
4263         assertThat(GroupHelper.getSection(notification_promotion).mName).isEqualTo(
4264                 "PromotionsSection");
4265 
4266         final NotificationChannel newsChannel = new NotificationChannel(NotificationChannel.NEWS_ID,
4267                 NotificationChannel.NEWS_ID, IMPORTANCE_LOW);
4268         final NotificationRecord notification_news = getNotificationRecord(mPkg, 0, "", mUser,
4269                 "", false, newsChannel);
4270         assertThat(GroupHelper.getSection(notification_news).mName).isEqualTo(
4271                 "NewsSection");
4272 
4273         final NotificationChannel socialChannel = new NotificationChannel(
4274                 NotificationChannel.SOCIAL_MEDIA_ID, NotificationChannel.SOCIAL_MEDIA_ID,
4275                 IMPORTANCE_LOW);
4276         final NotificationRecord notification_social = getNotificationRecord(mPkg, 0, "", mUser,
4277                 "", false, socialChannel);
4278         assertThat(GroupHelper.getSection(notification_social).mName).isEqualTo(
4279                 "SocialSection");
4280 
4281         final NotificationChannel recsChannel = new NotificationChannel(NotificationChannel.RECS_ID,
4282                 NotificationChannel.RECS_ID, IMPORTANCE_LOW);
4283         final NotificationRecord notification_recs = getNotificationRecord(mPkg, 0, "", mUser,
4284                 "", false, recsChannel);
4285         assertThat(GroupHelper.getSection(notification_recs).mName).isEqualTo(
4286                 "RecsSection");
4287 
4288         // Check that bundle categories with importance > IMPORTANCE_LOW are grouped into Alerting
4289         final NotificationChannel promoChannelAlerting = new NotificationChannel(
4290                 NotificationChannel.PROMOTIONS_ID, NotificationChannel.PROMOTIONS_ID,
4291                 IMPORTANCE_DEFAULT);
4292         final NotificationRecord notification_promotion_alerting = getNotificationRecord(mPkg, 0,
4293                 "", mUser, "", false, promoChannelAlerting);
4294         assertThat(GroupHelper.getSection(notification_promotion_alerting).mName).isEqualTo(
4295                 "AlertingSection");
4296 
4297         final NotificationChannel newsChannelAlerting = new NotificationChannel(
4298                 NotificationChannel.NEWS_ID, NotificationChannel.NEWS_ID, IMPORTANCE_DEFAULT);
4299         final NotificationRecord notification_news_alerting = getNotificationRecord(mPkg, 0, "",
4300                 mUser, "", false, newsChannelAlerting);
4301         assertThat(GroupHelper.getSection(notification_news_alerting).mName).isEqualTo(
4302                 "AlertingSection");
4303 
4304         final NotificationChannel socialChannelAlerting = new NotificationChannel(
4305                 NotificationChannel.SOCIAL_MEDIA_ID, NotificationChannel.SOCIAL_MEDIA_ID,
4306                 IMPORTANCE_DEFAULT);
4307         final NotificationRecord notification_social_alerting = getNotificationRecord(mPkg, 0, "",
4308                 mUser, "", false, socialChannelAlerting);
4309         assertThat(GroupHelper.getSection(notification_social_alerting).mName).isEqualTo(
4310                 "AlertingSection");
4311 
4312         final NotificationChannel recsChannelAlerting = new NotificationChannel(
4313                 NotificationChannel.RECS_ID, NotificationChannel.RECS_ID, IMPORTANCE_DEFAULT);
4314         final NotificationRecord notification_recs_alerting = getNotificationRecord(mPkg, 0, "",
4315                 mUser, "", false, recsChannelAlerting);
4316         assertThat(GroupHelper.getSection(notification_recs_alerting).mName).isEqualTo(
4317                 "AlertingSection");
4318     }
4319 
4320     @Test
4321     @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_FORCE_GROUP_CONVERSATIONS})
testNonGroupableNotifications_forceGroupConversations()4322     public void testNonGroupableNotifications_forceGroupConversations() {
4323         // Check that there is no valid section for: calls, foreground services, media notifications
4324         NotificationRecord notification_call = spy(getNotificationRecord(mPkg, 0, "", mUser,
4325                 "", false, IMPORTANCE_LOW));
4326         Notification n = mock(Notification.class);
4327         StatusBarNotification sbn = spy(getSbn("package", 0, "0", UserHandle.SYSTEM));
4328         when(notification_call.isConversation()).thenReturn(false);
4329         when(notification_call.getNotification()).thenReturn(n);
4330         when(notification_call.getSbn()).thenReturn(sbn);
4331         when(sbn.getNotification()).thenReturn(n);
4332         when(n.isStyle(Notification.CallStyle.class)).thenReturn(true);
4333         assertThat(GroupHelper.getSection(notification_call)).isNull();
4334 
4335         NotificationRecord notification_colorFg = spy(getNotificationRecord(mPkg, 0, "", mUser,
4336                 "", false, IMPORTANCE_LOW));
4337         sbn = spy(getSbn("package", 0, "0", UserHandle.SYSTEM));
4338         n = mock(Notification.class);
4339         when(notification_colorFg.isConversation()).thenReturn(false);
4340         when(notification_colorFg.getNotification()).thenReturn(n);
4341         when(notification_colorFg.getSbn()).thenReturn(sbn);
4342         when(sbn.getNotification()).thenReturn(n);
4343         when(n.isForegroundService()).thenReturn(true);
4344         when(n.isColorized()).thenReturn(true);
4345         when(n.isStyle(Notification.CallStyle.class)).thenReturn(false);
4346         assertThat(GroupHelper.getSection(notification_colorFg)).isNull();
4347 
4348         NotificationRecord notification_media = spy(getNotificationRecord(mPkg, 0, "", mUser,
4349                 "", false, IMPORTANCE_LOW));
4350         n = mock(Notification.class);
4351         sbn = spy(getSbn("package", 0, "0", UserHandle.SYSTEM));
4352         when(notification_media.isConversation()).thenReturn(false);
4353         when(notification_media.getNotification()).thenReturn(n);
4354         when(notification_media.getSbn()).thenReturn(sbn);
4355         when(sbn.getNotification()).thenReturn(n);
4356         when(n.isMediaNotification()).thenReturn(true);
4357         assertThat(GroupHelper.getSection(notification_media)).isNull();
4358     }
4359 
4360     @Test
4361     @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_FORCE_GROUP_CONVERSATIONS})
4362     @DisableFlags(FLAG_SORT_SECTION_BY_TIME)
testConversationGroupSections_disableSortSectionByTime()4363     public void testConversationGroupSections_disableSortSectionByTime() {
4364         // Check that there are separate sections for conversations: alerting and silent
4365         NotificationRecord notification_conversation_silent = getNotificationRecord(mPkg, 0, "",
4366                 mUser, "", false, IMPORTANCE_LOW);
4367         notification_conversation_silent = spy(notification_conversation_silent);
4368         when(notification_conversation_silent.isConversation()).thenReturn(true);
4369         assertThat(GroupHelper.getSection(notification_conversation_silent).mName).isEqualTo(
4370                 "PeopleSection(silent)");
4371 
4372         // Check that there is a correct section for conversations
4373         NotificationRecord notification_conversation_alerting = getNotificationRecord(mPkg, 0, "",
4374                 mUser, "", false, IMPORTANCE_DEFAULT);
4375         notification_conversation_alerting = spy(notification_conversation_alerting);
4376         when(notification_conversation_alerting.isConversation()).thenReturn(true);
4377         assertThat(GroupHelper.getSection(notification_conversation_alerting).mName).isEqualTo(
4378                 "PeopleSection(alerting)");
4379     }
4380 
4381     @Test
4382     @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
4383             FLAG_NOTIFICATION_FORCE_GROUP_CONVERSATIONS,
4384             FLAG_SORT_SECTION_BY_TIME})
testConversationGroupSections()4385     public void testConversationGroupSections() {
4386         // Check that there is a single section for silent/alerting conversations
4387         NotificationRecord notification_conversation_silent = getNotificationRecord(mPkg, 0, "",
4388                 mUser, "", false, IMPORTANCE_LOW);
4389         notification_conversation_silent = spy(notification_conversation_silent);
4390         when(notification_conversation_silent.isConversation()).thenReturn(true);
4391         assertThat(GroupHelper.getSection(notification_conversation_silent).mName).isEqualTo(
4392                 "PeopleSection");
4393 
4394         NotificationRecord notification_conversation_alerting = getNotificationRecord(mPkg, 0, "",
4395                 mUser, "", false, IMPORTANCE_DEFAULT);
4396         notification_conversation_alerting = spy(notification_conversation_alerting);
4397         when(notification_conversation_alerting.isConversation()).thenReturn(true);
4398         assertThat(GroupHelper.getSection(notification_conversation_alerting).mName).isEqualTo(
4399                 "PeopleSection");
4400 
4401         // Check that there is a section for priority conversations
4402         NotificationRecord notification_conversation_prio = getNotificationRecord(mPkg, 0, "",
4403                 mUser, "", false, IMPORTANCE_DEFAULT);
4404         notification_conversation_prio = spy(notification_conversation_prio);
4405         when(notification_conversation_prio.isConversation()).thenReturn(true);
4406         notification_conversation_prio.getChannel().setImportantConversation(true);
4407         assertThat(GroupHelper.getSection(notification_conversation_prio).mName).isEqualTo(
4408                 "PeopleSection(priority)");
4409     }
4410 
4411     @Test
4412     @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
onNotificationRemoved_lastChildOfCachedSummary_firesCachedSummaryDeleteIntent()4413     public void onNotificationRemoved_lastChildOfCachedSummary_firesCachedSummaryDeleteIntent() {
4414         final List<NotificationRecord> notificationList = new ArrayList<>();
4415         final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>();
4416         final String pkg = "package";
4417         NotificationRecord onlyChildOfFirstGroup = null;
4418         PendingIntent deleteIntentofFirstSummary = PendingIntent.getActivity(mContext, 1,
4419                 new Intent(), PendingIntent.FLAG_IMMUTABLE);
4420         // Post singleton groups, above forced group limit, so they are force grouped
4421         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
4422             NotificationRecord summary = getNotificationRecord(pkg, i,
4423                     String.valueOf(i), UserHandle.SYSTEM, "testGrp " + i, true);
4424             notificationList.add(summary);
4425             NotificationRecord child = getNotificationRecord(pkg, i + 42,
4426                     String.valueOf(i + 42), UserHandle.SYSTEM, "testGrp " + i, false);
4427             notificationList.add(child);
4428             summaryByGroup.put(summary.getGroupKey(), summary);
4429             if (i == 0) {
4430                 onlyChildOfFirstGroup = child;
4431                 summary.getNotification().deleteIntent = deleteIntentofFirstSummary;
4432             }
4433             mGroupHelper.onNotificationPostedWithDelay(child, notificationList, summaryByGroup);
4434             summary.isCanceled = true;  // simulate removing the app summary
4435             mGroupHelper.onNotificationPostedWithDelay(summary, notificationList, summaryByGroup);
4436         }
4437         // Sparse group autogrouping would've removed the summary.
4438         notificationList.remove(0);
4439 
4440         // Now remove the only child of the first (force-grouped, cuz sparse) group.
4441         notificationList.remove(0);
4442         onlyChildOfFirstGroup.isCanceled = true;
4443         mGroupHelper.onNotificationRemoved(onlyChildOfFirstGroup, notificationList, true);
4444 
4445         verify(mCallback).sendAppProvidedSummaryDeleteIntent(eq(pkg),
4446                 eq(deleteIntentofFirstSummary));
4447     }
4448 
4449     @Test
4450     @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
testGroupSummaryAdded_hadUngroupedNotif_doesNotAutogroup()4451     public void testGroupSummaryAdded_hadUngroupedNotif_doesNotAutogroup() {
4452         // Scenario:
4453         //  * child notification posted before summary; added to ungrouped notifications
4454         //  * summary posted, so now the child has a group summary and is no longer "ungrouped
4455         //  * another ungrouped notification is posted
4456         // Confirm that the first notification (that now has a summary) is not autogrouped.
4457 
4458         // Bookkeeping items
4459         List<NotificationRecord> notifList = new ArrayList<>();
4460         Map<String, NotificationRecord> summaryByGroupKey = new HashMap<>();
4461 
4462         // Setup: post AUTOGROUP_AT_COUNT - 2 notifications so that the next notification would not
4463         // trigger autogrouping, but the one after that would
4464         for (int i = 0; i < AUTOGROUP_AT_COUNT - 2; i++) {
4465             NotificationRecord child = getNotificationRecord(mPkg, i, "", mUser, "group" + i, false,
4466                     IMPORTANCE_DEFAULT);
4467             notifList.add(child);
4468             mGroupHelper.onNotificationPostedWithDelay(child, notifList, summaryByGroupKey);
4469         }
4470 
4471         // Group child: posted enough before its associated summary to be put in the "ungrouped"
4472         // set of notifications
4473         NotificationRecord groupChild = getNotificationRecord(mPkg, AUTOGROUP_AT_COUNT - 2, "",
4474                 mUser, "specialGroup", false, IMPORTANCE_DEFAULT);
4475         notifList.add(groupChild);
4476         mGroupHelper.onNotificationPostedWithDelay(groupChild, notifList, summaryByGroupKey);
4477 
4478         // Group summary: posted after child 1
4479         NotificationRecord groupSummary = getNotificationRecord(mPkg, AUTOGROUP_AT_COUNT - 1, "",
4480                 mUser, "specialGroup", true, IMPORTANCE_DEFAULT);
4481         notifList.add(groupSummary);
4482         summaryByGroupKey.put(groupSummary.getSbn().getGroupKey(), groupSummary);
4483         mGroupHelper.onGroupSummaryAdded(groupSummary, notifList);
4484         mGroupHelper.onNotificationPostedWithDelay(groupSummary, notifList, summaryByGroupKey);
4485 
4486         // One more notification posted to the group; because its summary already exists, it should
4487         // never be counted as an "ungrouped" notification
4488         NotificationRecord groupChild2 = getNotificationRecord(mPkg, AUTOGROUP_AT_COUNT, "",
4489                 mUser, "specialGroup", false, IMPORTANCE_DEFAULT);
4490         notifList.add(groupChild2);
4491         mGroupHelper.onNotificationPostedWithDelay(groupChild2, notifList, summaryByGroupKey);
4492 
4493         // Now one more ungrouped notification; this would have put the number of "ungrouped"
4494         // notifications above the limit if the first groupChild notification were left ungrouped
4495         NotificationRecord extra = getNotificationRecord(mPkg, AUTOGROUP_AT_COUNT + 1, "", mUser,
4496                 "yetAnotherGroup", false, IMPORTANCE_DEFAULT);
4497         notifList.add(extra);
4498         mGroupHelper.onNotificationPostedWithDelay(extra, notifList, summaryByGroupKey);
4499 
4500         // no autogrouping should have occurred
4501         verifyNoMoreInteractions(mCallback);
4502     }
4503 
4504     @Test
4505     @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
testGroupSummaryAdded_onlyUnrelatedGroupedNotifs()4506     public void testGroupSummaryAdded_onlyUnrelatedGroupedNotifs() {
4507         // If all of the existing ungrouped notifications have nothing to do with the summary
4508         // they should still get grouped as needed.
4509         List<NotificationRecord> notifList = new ArrayList<>();
4510         Map<String, NotificationRecord> summaryByGroupKey = new HashMap<>();
4511 
4512         // Post 1 fewer than the autogroupable notifications, each associated with a different
4513         // group without a summary.
4514         for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
4515             NotificationRecord child = getNotificationRecord(mPkg, i, "", mUser, "group" + i, false,
4516                     IMPORTANCE_DEFAULT);
4517             notifList.add(child);
4518             mGroupHelper.onNotificationPostedWithDelay(child, notifList, summaryByGroupKey);
4519         }
4520 
4521         // At this point we do not yet expect autogrouping.
4522         // Add a group summary that is a summary associated with none of the above notifications.
4523         // Because this gets considered a "summary without children", all of these notifications
4524         // should now be autogrouped.
4525         NotificationRecord summary = getNotificationRecord(mPkg, AUTOGROUP_AT_COUNT, "", mUser,
4526                 "summaryGroup", true, IMPORTANCE_DEFAULT);
4527         notifList.add(summary);
4528         summaryByGroupKey.put(summary.getSbn().getKey(), summary);
4529         mGroupHelper.onGroupSummaryAdded(summary, notifList);
4530         mGroupHelper.onNotificationPostedWithDelay(summary, notifList, summaryByGroupKey);
4531 
4532         // all of the above posted notifications should be autogrouped
4533         String expectedGroupKey = getExpectedAutogroupKey(
4534                 getNotificationRecord(mPkg, 0, String.valueOf(0), mUser));
4535         verify(mCallback, times(1)).addAutoGroupSummary(
4536                 anyInt(), eq(mPkg), anyString(), eq(expectedGroupKey),
4537                 anyInt(), eq(getNotificationAttributes(BASE_FLAGS)));
4538         verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(),
4539                 eq(expectedGroupKey), anyBoolean());
4540     }
4541 }
4542