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