RankingHelperTest.java revision 41103f42126acbd602de81ac87a2e605492ff528
1/* 2 * Copyright (C) 2014 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 */ 16package com.android.server.notification; 17 18import static android.app.NotificationManager.IMPORTANCE_DEFAULT; 19import static android.app.NotificationManager.IMPORTANCE_HIGH; 20import static android.app.NotificationManager.IMPORTANCE_LOW; 21 22import static junit.framework.Assert.assertNull; 23import static junit.framework.Assert.fail; 24 25import org.json.JSONArray; 26import org.json.JSONObject; 27import org.junit.Before; 28import org.junit.Test; 29import org.junit.runner.RunWith; 30 31import com.android.internal.util.FastXmlSerializer; 32 33import org.mockito.Mock; 34import org.mockito.MockitoAnnotations; 35import org.xmlpull.v1.XmlPullParser; 36import org.xmlpull.v1.XmlSerializer; 37 38import android.app.Notification; 39import android.app.NotificationChannelGroup; 40import android.content.Context; 41import android.app.NotificationChannel; 42import android.app.NotificationManager; 43import android.content.pm.ApplicationInfo; 44import android.content.pm.PackageManager; 45import android.graphics.Color; 46import android.media.AudioAttributes; 47import android.net.Uri; 48import android.os.Build; 49import android.os.UserHandle; 50import android.service.notification.StatusBarNotification; 51import android.support.test.InstrumentationRegistry; 52import android.support.test.runner.AndroidJUnit4; 53import android.test.suitebuilder.annotation.SmallTest; 54import android.util.ArrayMap; 55import android.util.Xml; 56 57import java.io.BufferedInputStream; 58import java.io.BufferedOutputStream; 59import java.io.ByteArrayInputStream; 60import java.io.ByteArrayOutputStream; 61import java.util.ArrayList; 62import java.util.Arrays; 63import java.util.HashMap; 64import java.util.List; 65import java.util.Map; 66import java.util.Objects; 67import java.util.concurrent.ThreadLocalRandom; 68 69import static org.junit.Assert.assertEquals; 70import static org.junit.Assert.assertFalse; 71import static org.junit.Assert.assertNotNull; 72import static org.junit.Assert.assertTrue; 73import static org.mockito.Matchers.anyInt; 74import static org.mockito.Matchers.anyString; 75import static org.mockito.Matchers.eq; 76import static org.mockito.Mockito.when; 77 78@SmallTest 79@RunWith(AndroidJUnit4.class) 80public class RankingHelperTest { 81 @Mock 82 NotificationUsageStats mUsageStats; 83 @Mock 84 RankingHandler handler; 85 @Mock 86 PackageManager mPm; 87 88 private Notification mNotiGroupGSortA; 89 private Notification mNotiGroupGSortB; 90 private Notification mNotiNoGroup; 91 private Notification mNotiNoGroup2; 92 private Notification mNotiNoGroupSortA; 93 private NotificationRecord mRecordGroupGSortA; 94 private NotificationRecord mRecordGroupGSortB; 95 private NotificationRecord mRecordNoGroup; 96 private NotificationRecord mRecordNoGroup2; 97 private NotificationRecord mRecordNoGroupSortA; 98 private RankingHelper mHelper; 99 private final String pkg = "com.android.server.notification"; 100 private final int uid = 0; 101 private final String pkg2 = "pkg2"; 102 private final int uid2 = 1111111; 103 private static final String TEST_CHANNEL_ID = "test_channel_id"; 104 private AudioAttributes mAudioAttributes; 105 106 private Context getContext() { 107 return InstrumentationRegistry.getTargetContext(); 108 } 109 110 @Before 111 public void setUp() { 112 MockitoAnnotations.initMocks(this); 113 UserHandle user = UserHandle.ALL; 114 115 mHelper = new RankingHelper(getContext(), mPm, handler, mUsageStats, 116 new String[]{ImportanceExtractor.class.getName()}); 117 118 mNotiGroupGSortA = new Notification.Builder(getContext(), TEST_CHANNEL_ID) 119 .setContentTitle("A") 120 .setGroup("G") 121 .setSortKey("A") 122 .setWhen(1205) 123 .build(); 124 mRecordGroupGSortA = new NotificationRecord(getContext(), new StatusBarNotification( 125 "package", "package", 1, null, 0, 0, mNotiGroupGSortA, user, 126 null, System.currentTimeMillis()), getDefaultChannel()); 127 128 mNotiGroupGSortB = new Notification.Builder(getContext(), TEST_CHANNEL_ID) 129 .setContentTitle("B") 130 .setGroup("G") 131 .setSortKey("B") 132 .setWhen(1200) 133 .build(); 134 mRecordGroupGSortB = new NotificationRecord(getContext(), new StatusBarNotification( 135 "package", "package", 1, null, 0, 0, mNotiGroupGSortB, user, 136 null, System.currentTimeMillis()), getDefaultChannel()); 137 138 mNotiNoGroup = new Notification.Builder(getContext(), TEST_CHANNEL_ID) 139 .setContentTitle("C") 140 .setWhen(1201) 141 .build(); 142 mRecordNoGroup = new NotificationRecord(getContext(), new StatusBarNotification( 143 "package", "package", 1, null, 0, 0, mNotiNoGroup, user, 144 null, System.currentTimeMillis()), getDefaultChannel()); 145 146 mNotiNoGroup2 = new Notification.Builder(getContext(), TEST_CHANNEL_ID) 147 .setContentTitle("D") 148 .setWhen(1202) 149 .build(); 150 mRecordNoGroup2 = new NotificationRecord(getContext(), new StatusBarNotification( 151 "package", "package", 1, null, 0, 0, mNotiNoGroup2, user, 152 null, System.currentTimeMillis()), getDefaultChannel()); 153 154 mNotiNoGroupSortA = new Notification.Builder(getContext(), TEST_CHANNEL_ID) 155 .setContentTitle("E") 156 .setWhen(1201) 157 .setSortKey("A") 158 .build(); 159 mRecordNoGroupSortA = new NotificationRecord(getContext(), new StatusBarNotification( 160 "package", "package", 1, null, 0, 0, mNotiNoGroupSortA, user, 161 null, System.currentTimeMillis()), getDefaultChannel()); 162 163 mAudioAttributes = new AudioAttributes.Builder() 164 .setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN) 165 .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE) 166 .setFlags(AudioAttributes.FLAG_AUDIBILITY_ENFORCED) 167 .build(); 168 169 final ApplicationInfo legacy = new ApplicationInfo(); 170 legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1; 171 final ApplicationInfo upgrade = new ApplicationInfo(); 172 upgrade.targetSdkVersion = Build.VERSION_CODES.N_MR1 + 1; 173 try { 174 when(mPm.getApplicationInfoAsUser(eq(pkg), anyInt(), anyInt())).thenReturn(legacy); 175 when(mPm.getApplicationInfoAsUser(eq(pkg2), anyInt(), anyInt())).thenReturn(upgrade); 176 } catch (PackageManager.NameNotFoundException e) { 177 } 178 } 179 180 private NotificationChannel getDefaultChannel() { 181 return new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "name", 182 IMPORTANCE_LOW); 183 } 184 185 private ByteArrayOutputStream writeXmlAndPurge(String pkg, int uid, String... channelIds) 186 throws Exception { 187 XmlSerializer serializer = new FastXmlSerializer(); 188 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 189 serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); 190 serializer.startDocument(null, true); 191 serializer.startTag(null, "ranking"); 192 mHelper.writeXml(serializer, false); 193 serializer.endTag(null, "ranking"); 194 serializer.endDocument(); 195 serializer.flush(); 196 197 for (String channelId : channelIds) { 198 mHelper.permanentlyDeleteNotificationChannel(pkg, uid, channelId); 199 } 200 return baos; 201 } 202 203 private void compareChannels(NotificationChannel expected, NotificationChannel actual) { 204 assertEquals(expected.getId(), actual.getId()); 205 assertEquals(expected.getName(), actual.getName()); 206 assertEquals(expected.shouldVibrate(), actual.shouldVibrate()); 207 assertEquals(expected.shouldShowLights(), actual.shouldShowLights()); 208 assertEquals(expected.getImportance(), actual.getImportance()); 209 assertEquals(expected.getLockscreenVisibility(), actual.getLockscreenVisibility()); 210 assertEquals(expected.getSound(), actual.getSound()); 211 assertEquals(expected.canBypassDnd(), actual.canBypassDnd()); 212 assertTrue(Arrays.equals(expected.getVibrationPattern(), actual.getVibrationPattern())); 213 assertEquals(expected.getGroup(), actual.getGroup()); 214 assertEquals(expected.getAudioAttributes(), actual.getAudioAttributes()); 215 assertEquals(expected.getLightColor(), actual.getLightColor()); 216 } 217 218 private void compareGroups(NotificationChannelGroup expected, NotificationChannelGroup actual) { 219 assertEquals(expected.getId(), actual.getId()); 220 assertEquals(expected.getName(), actual.getName()); 221 } 222 223 @Test 224 public void testFindAfterRankingWithASplitGroup() throws Exception { 225 ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(3); 226 notificationList.add(mRecordGroupGSortA); 227 notificationList.add(mRecordGroupGSortB); 228 notificationList.add(mRecordNoGroup); 229 notificationList.add(mRecordNoGroupSortA); 230 mHelper.sort(notificationList); 231 assertTrue(mHelper.indexOf(notificationList, mRecordGroupGSortA) >= 0); 232 assertTrue(mHelper.indexOf(notificationList, mRecordGroupGSortB) >= 0); 233 assertTrue(mHelper.indexOf(notificationList, mRecordNoGroup) >= 0); 234 assertTrue(mHelper.indexOf(notificationList, mRecordNoGroupSortA) >= 0); 235 } 236 237 @Test 238 public void testSortShouldNotThrowWithPlainNotifications() throws Exception { 239 ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(2); 240 notificationList.add(mRecordNoGroup); 241 notificationList.add(mRecordNoGroup2); 242 mHelper.sort(notificationList); 243 } 244 245 @Test 246 public void testSortShouldNotThrowOneSorted() throws Exception { 247 ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(2); 248 notificationList.add(mRecordNoGroup); 249 notificationList.add(mRecordNoGroupSortA); 250 mHelper.sort(notificationList); 251 } 252 253 @Test 254 public void testSortShouldNotThrowOneNotification() throws Exception { 255 ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(1); 256 notificationList.add(mRecordNoGroup); 257 mHelper.sort(notificationList); 258 } 259 260 @Test 261 public void testSortShouldNotThrowOneSortKey() throws Exception { 262 ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(1); 263 notificationList.add(mRecordGroupGSortB); 264 mHelper.sort(notificationList); 265 } 266 267 @Test 268 public void testSortShouldNotThrowOnEmptyList() throws Exception { 269 ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(); 270 mHelper.sort(notificationList); 271 } 272 273 @Test 274 public void testChannelXml() throws Exception { 275 NotificationChannelGroup ncg = new NotificationChannelGroup("1", "bye"); 276 NotificationChannelGroup ncg2 = new NotificationChannelGroup("2", "hello"); 277 NotificationChannel channel1 = 278 new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH); 279 NotificationChannel channel2 = 280 new NotificationChannel("id2", "name2", IMPORTANCE_LOW); 281 channel2.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes); 282 channel2.enableLights(true); 283 channel2.setBypassDnd(true); 284 channel2.setLockscreenVisibility(Notification.VISIBILITY_SECRET); 285 channel2.enableVibration(true); 286 channel2.setGroup(ncg.getId()); 287 channel2.setVibrationPattern(new long[]{100, 67, 145, 156}); 288 channel2.setLightColor(Color.BLUE); 289 290 mHelper.createNotificationChannelGroup(pkg, uid, ncg, true); 291 mHelper.createNotificationChannelGroup(pkg, uid, ncg2, true); 292 mHelper.createNotificationChannel(pkg, uid, channel1, true); 293 mHelper.createNotificationChannel(pkg, uid, channel2, false); 294 295 mHelper.setShowBadge(pkg, uid, true); 296 mHelper.setShowBadge(pkg2, uid2, false); 297 298 ByteArrayOutputStream baos = writeXmlAndPurge(pkg, uid, channel1.getId(), channel2.getId(), 299 NotificationChannel.DEFAULT_CHANNEL_ID); 300 mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{pkg}, new int[]{uid}); 301 302 XmlPullParser parser = Xml.newPullParser(); 303 parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())), 304 null); 305 parser.nextTag(); 306 mHelper.readXml(parser, false); 307 308 assertFalse(mHelper.canShowBadge(pkg2, uid2)); 309 assertTrue(mHelper.canShowBadge(pkg, uid)); 310 assertEquals(channel1, mHelper.getNotificationChannel(pkg, uid, channel1.getId(), false)); 311 compareChannels(channel2, 312 mHelper.getNotificationChannel(pkg, uid, channel2.getId(), false)); 313 assertNotNull(mHelper.getNotificationChannel( 314 pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID, false)); 315 316 List<NotificationChannelGroup> actualGroups = 317 mHelper.getNotificationChannelGroups(pkg, uid, false).getList(); 318 boolean foundNcg = false; 319 for (NotificationChannelGroup actual : actualGroups) { 320 if (ncg.getId().equals(actual.getId())) { 321 foundNcg = true; 322 compareGroups(ncg, actual); 323 } else if (ncg2.getId().equals(actual.getId())) { 324 compareGroups(ncg2, actual); 325 } 326 } 327 assertTrue(foundNcg); 328 329 boolean foundChannel2Group = false; 330 for (NotificationChannelGroup actual : actualGroups) { 331 if (channel2.getGroup().equals(actual.getChannels().get(0).getGroup())) { 332 foundChannel2Group = true; 333 break; 334 } 335 } 336 assertTrue(foundChannel2Group); 337 } 338 339 @Test 340 public void testChannelXml_defaultChannelLegacyApp_noUserSettings() throws Exception { 341 NotificationChannel channel1 = 342 new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_DEFAULT); 343 344 mHelper.createNotificationChannel(pkg, uid, channel1, true); 345 346 ByteArrayOutputStream baos = writeXmlAndPurge(pkg, uid, channel1.getId(), 347 NotificationChannel.DEFAULT_CHANNEL_ID); 348 349 XmlPullParser parser = Xml.newPullParser(); 350 parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())), 351 null); 352 parser.nextTag(); 353 mHelper.readXml(parser, false); 354 355 final NotificationChannel updated = mHelper.getNotificationChannel( 356 pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID, false); 357 assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, updated.getImportance()); 358 assertFalse(updated.canBypassDnd()); 359 assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE, updated.getLockscreenVisibility()); 360 assertEquals(0, updated.getUserLockedFields()); 361 } 362 363 @Test 364 public void testChannelXml_defaultChannelUpdatedApp_userSettings() throws Exception { 365 NotificationChannel channel1 = 366 new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_MIN); 367 mHelper.createNotificationChannel(pkg, uid, channel1, true); 368 369 final NotificationChannel defaultChannel = mHelper.getNotificationChannel( 370 pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID, false); 371 defaultChannel.setImportance(IMPORTANCE_LOW); 372 mHelper.updateNotificationChannel(pkg, uid, defaultChannel); 373 374 ByteArrayOutputStream baos = writeXmlAndPurge(pkg, uid, channel1.getId(), 375 NotificationChannel.DEFAULT_CHANNEL_ID); 376 377 XmlPullParser parser = Xml.newPullParser(); 378 parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())), 379 null); 380 parser.nextTag(); 381 mHelper.readXml(parser, false); 382 383 assertEquals(IMPORTANCE_LOW, mHelper.getNotificationChannel( 384 pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID, false).getImportance()); 385 } 386 387 @Test 388 public void testChannelXml_upgradeCreateDefaultChannel() throws Exception { 389 final String preupgradeXml = "<ranking version=\"1\">\n" 390 + "<package name=\"" + pkg + "\" importance=\"" 391 + NotificationManager.IMPORTANCE_HIGH 392 + "\" priority=\"" + Notification.PRIORITY_MAX + "\" visibility=\"" 393 + Notification.VISIBILITY_SECRET + "\"" + " uid=\"" + uid + "\" />\n" 394 + "<package name=\"" + pkg2 + "\" uid=\"" + uid2 + "\" visibility=\"" 395 + Notification.VISIBILITY_PRIVATE + "\" />\n" 396 + "</ranking>"; 397 XmlPullParser parser = Xml.newPullParser(); 398 parser.setInput(new BufferedInputStream(new ByteArrayInputStream(preupgradeXml.getBytes())), 399 null); 400 parser.nextTag(); 401 mHelper.readXml(parser, false); 402 403 final NotificationChannel updated1 = mHelper.getNotificationChannel( 404 pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID, false); 405 assertEquals(NotificationManager.IMPORTANCE_HIGH, updated1.getImportance()); 406 assertTrue(updated1.canBypassDnd()); 407 assertEquals(Notification.VISIBILITY_SECRET, updated1.getLockscreenVisibility()); 408 assertEquals(NotificationChannel.USER_LOCKED_IMPORTANCE 409 | NotificationChannel.USER_LOCKED_PRIORITY 410 | NotificationChannel.USER_LOCKED_VISIBILITY, updated1.getUserLockedFields()); 411 412 final NotificationChannel updated2 = mHelper.getNotificationChannel( 413 pkg2, uid2, NotificationChannel.DEFAULT_CHANNEL_ID, false); 414 // clamped 415 assertEquals(IMPORTANCE_LOW, updated2.getImportance()); 416 assertFalse(updated2.canBypassDnd()); 417 assertEquals(Notification.VISIBILITY_PRIVATE, updated2.getLockscreenVisibility()); 418 assertEquals(NotificationChannel.USER_LOCKED_VISIBILITY, updated2.getUserLockedFields()); 419 } 420 421 @Test 422 public void testCreateChannel_blocked() throws Exception { 423 mHelper.setImportance(pkg, uid, NotificationManager.IMPORTANCE_NONE); 424 425 mHelper.createNotificationChannel(pkg, uid, 426 new NotificationChannel(pkg, "bananas", IMPORTANCE_LOW), true); 427 } 428 429 @Test 430 public void testUpdate_userLockedImportance() throws Exception { 431 // all fields locked by user 432 final NotificationChannel channel = 433 new NotificationChannel("id2", "name2", IMPORTANCE_LOW); 434 channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE); 435 436 mHelper.createNotificationChannel(pkg, uid, channel, false); 437 438 // same id, try to update 439 final NotificationChannel channel2 = 440 new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH); 441 442 mHelper.updateNotificationChannelFromAssistant(pkg, uid, channel2); 443 444 // no fields should be changed 445 assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId(), false)); 446 } 447 448 @Test 449 public void testUpdate_userLockedVisibility() throws Exception { 450 // all fields locked by user 451 final NotificationChannel channel = 452 new NotificationChannel("id2", "name2", IMPORTANCE_LOW); 453 channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET); 454 channel.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY); 455 456 mHelper.createNotificationChannel(pkg, uid, channel, false); 457 458 // same id, try to update 459 final NotificationChannel channel2 = 460 new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH); 461 channel2.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); 462 463 mHelper.updateNotificationChannelFromAssistant(pkg, uid, channel2); 464 465 // no fields should be changed 466 assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId(), false)); 467 } 468 469 @Test 470 public void testUpdate_userLockedVibration() throws Exception { 471 // all fields locked by user 472 final NotificationChannel channel = 473 new NotificationChannel("id2", "name2", IMPORTANCE_LOW); 474 channel.enableLights(false); 475 channel.lockFields(NotificationChannel.USER_LOCKED_VIBRATION); 476 477 mHelper.createNotificationChannel(pkg, uid, channel, false); 478 479 // same id, try to update 480 final NotificationChannel channel2 = 481 new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH); 482 channel2.enableVibration(true); 483 channel2.setVibrationPattern(new long[]{100}); 484 485 mHelper.updateNotificationChannelFromAssistant(pkg, uid, channel2); 486 487 // no fields should be changed 488 assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId(), false)); 489 } 490 491 @Test 492 public void testUpdate_userLockedLights() throws Exception { 493 // all fields locked by user 494 final NotificationChannel channel = 495 new NotificationChannel("id2", "name2", IMPORTANCE_LOW); 496 channel.enableLights(false); 497 channel.lockFields(NotificationChannel.USER_LOCKED_LIGHTS); 498 499 mHelper.createNotificationChannel(pkg, uid, channel, false); 500 501 // same id, try to update 502 final NotificationChannel channel2 = 503 new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH); 504 channel2.enableLights(true); 505 506 mHelper.updateNotificationChannelFromAssistant(pkg, uid, channel2); 507 508 // no fields should be changed 509 assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId(), false)); 510 } 511 512 @Test 513 public void testUpdate_userLockedPriority() throws Exception { 514 // all fields locked by user 515 final NotificationChannel channel = 516 new NotificationChannel("id2", "name2", IMPORTANCE_LOW); 517 channel.setBypassDnd(true); 518 channel.lockFields(NotificationChannel.USER_LOCKED_PRIORITY); 519 520 mHelper.createNotificationChannel(pkg, uid, channel, false); 521 522 // same id, try to update all fields 523 final NotificationChannel channel2 = 524 new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH); 525 channel2.setBypassDnd(false); 526 527 mHelper.updateNotificationChannelFromAssistant(pkg, uid, channel2); 528 529 // no fields should be changed 530 assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId(), false)); 531 } 532 533 @Test 534 public void testUpdate_userLockedRingtone() throws Exception { 535 // all fields locked by user 536 final NotificationChannel channel = 537 new NotificationChannel("id2", "name2", IMPORTANCE_LOW); 538 channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes); 539 channel.lockFields(NotificationChannel.USER_LOCKED_SOUND); 540 541 mHelper.createNotificationChannel(pkg, uid, channel, false); 542 543 // same id, try to update all fields 544 final NotificationChannel channel2 = 545 new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH); 546 channel2.setSound(new Uri.Builder().scheme("test2").build(), mAudioAttributes); 547 548 mHelper.updateNotificationChannelFromAssistant(pkg, uid, channel2); 549 550 // no fields should be changed 551 assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId(), false)); 552 } 553 554 @Test 555 public void testUpdate_userLockedBadge() throws Exception { 556 final NotificationChannel channel = 557 new NotificationChannel("id2", "name2", IMPORTANCE_LOW); 558 channel.setShowBadge(true); 559 channel.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE); 560 561 mHelper.createNotificationChannel(pkg, uid, channel, false); 562 563 final NotificationChannel channel2 = 564 new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH); 565 channel2.setShowBadge(false); 566 567 mHelper.updateNotificationChannelFromAssistant(pkg, uid, channel2); 568 569 // no fields should be changed 570 assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId(), false)); 571 } 572 573 @Test 574 public void testUpdate() throws Exception { 575 // no fields locked by user 576 final NotificationChannel channel = 577 new NotificationChannel("id2", "name2", IMPORTANCE_LOW); 578 channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes); 579 channel.enableLights(true); 580 channel.setBypassDnd(true); 581 channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET); 582 583 mHelper.createNotificationChannel(pkg, uid, channel, false); 584 585 // same id, try to update all fields 586 final NotificationChannel channel2 = 587 new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH); 588 channel2.setSound(new Uri.Builder().scheme("test2").build(), mAudioAttributes); 589 channel2.enableLights(false); 590 channel2.setBypassDnd(false); 591 channel2.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); 592 593 mHelper.updateNotificationChannel(pkg, uid, channel2); 594 595 // all fields should be changed 596 assertEquals(channel2, mHelper.getNotificationChannel(pkg, uid, channel.getId(), false)); 597 } 598 599 @Test 600 public void testGetChannelWithFallback() throws Exception { 601 NotificationChannel channel = 602 mHelper.getNotificationChannelWithFallback(pkg, uid, "garbage", false); 603 assertEquals(NotificationChannel.DEFAULT_CHANNEL_ID, channel.getId()); 604 } 605 606 @Test 607 public void testCreateChannel_CannotChangeHiddenFields() throws Exception { 608 final NotificationChannel channel = 609 new NotificationChannel("id2", "name2", IMPORTANCE_LOW); 610 channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes); 611 channel.enableLights(true); 612 channel.setBypassDnd(true); 613 channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET); 614 channel.setShowBadge(true); 615 int lockMask = 0; 616 for (int i = 0; i < NotificationChannel.LOCKABLE_FIELDS.length; i++) { 617 lockMask |= NotificationChannel.LOCKABLE_FIELDS[i]; 618 } 619 channel.lockFields(lockMask); 620 621 mHelper.createNotificationChannel(pkg, uid, channel, true); 622 623 NotificationChannel savedChannel = 624 mHelper.getNotificationChannel(pkg, uid, channel.getId(), false); 625 626 assertEquals(channel.getName(), savedChannel.getName()); 627 assertEquals(channel.shouldShowLights(), savedChannel.shouldShowLights()); 628 assertFalse(savedChannel.canBypassDnd()); 629 assertFalse(Notification.VISIBILITY_SECRET == savedChannel.getLockscreenVisibility()); 630 assertEquals(channel.canShowBadge(), savedChannel.canShowBadge()); 631 } 632 633 @Test 634 public void testCreateChannel_CannotChangeHiddenFieldsAssistant() throws Exception { 635 final NotificationChannel channel = 636 new NotificationChannel("id2", "name2", IMPORTANCE_LOW); 637 channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes); 638 channel.enableLights(true); 639 channel.setBypassDnd(true); 640 channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET); 641 channel.setShowBadge(true); 642 int lockMask = 0; 643 for (int i = 0; i < NotificationChannel.LOCKABLE_FIELDS.length; i++) { 644 lockMask |= NotificationChannel.LOCKABLE_FIELDS[i]; 645 } 646 channel.lockFields(lockMask); 647 648 mHelper.createNotificationChannel(pkg, uid, channel, true); 649 650 NotificationChannel savedChannel = 651 mHelper.getNotificationChannel(pkg, uid, channel.getId(), false); 652 653 assertEquals(channel.getName(), savedChannel.getName()); 654 assertEquals(channel.shouldShowLights(), savedChannel.shouldShowLights()); 655 assertFalse(savedChannel.canBypassDnd()); 656 assertFalse(Notification.VISIBILITY_SECRET == savedChannel.getLockscreenVisibility()); 657 assertEquals(channel.canShowBadge(), savedChannel.canShowBadge()); 658 } 659 660 @Test 661 public void testGetDeletedChannel() throws Exception { 662 NotificationChannel channel = 663 new NotificationChannel("id2", "name2", IMPORTANCE_LOW); 664 channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes); 665 channel.enableLights(true); 666 channel.setBypassDnd(true); 667 channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET); 668 channel.enableVibration(true); 669 channel.setVibrationPattern(new long[]{100, 67, 145, 156}); 670 671 mHelper.createNotificationChannel(pkg, uid, channel, true); 672 mHelper.deleteNotificationChannel(pkg, uid, channel.getId()); 673 674 // Does not return deleted channel 675 NotificationChannel response = 676 mHelper.getNotificationChannel(pkg, uid, channel.getId(), false); 677 assertNull(response); 678 679 // Returns deleted channel 680 response = mHelper.getNotificationChannel(pkg, uid, channel.getId(), true); 681 compareChannels(channel, response); 682 assertTrue(response.isDeleted()); 683 } 684 685 @Test 686 public void testGetDeletedChannels() throws Exception { 687 Map<String, NotificationChannel> channelMap = new HashMap<>(); 688 NotificationChannel channel = 689 new NotificationChannel("id2", "name2", IMPORTANCE_LOW); 690 channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes); 691 channel.enableLights(true); 692 channel.setBypassDnd(true); 693 channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE); 694 channel.enableVibration(true); 695 channel.setVibrationPattern(new long[]{100, 67, 145, 156}); 696 channelMap.put(channel.getId(), channel); 697 NotificationChannel channel2 = 698 new NotificationChannel("id4", "a", NotificationManager.IMPORTANCE_HIGH); 699 channelMap.put(channel2.getId(), channel2); 700 mHelper.createNotificationChannel(pkg, uid, channel, true); 701 mHelper.createNotificationChannel(pkg, uid, channel2, true); 702 703 mHelper.deleteNotificationChannel(pkg, uid, channel.getId()); 704 705 // Returns only non-deleted channels 706 List<NotificationChannel> channels = 707 mHelper.getNotificationChannels(pkg, uid, false).getList(); 708 assertEquals(2, channels.size()); // Default channel + non-deleted channel 709 for (NotificationChannel nc : channels) { 710 if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(nc.getId())) { 711 compareChannels(channel2, nc); 712 } 713 } 714 715 // Returns deleted channels too 716 channels = mHelper.getNotificationChannels(pkg, uid, true).getList(); 717 assertEquals(3, channels.size()); // Includes default channel 718 for (NotificationChannel nc : channels) { 719 if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(nc.getId())) { 720 compareChannels(channelMap.get(nc.getId()), nc); 721 } 722 } 723 } 724 725 @Test 726 public void testGetDeletedChannelCount() throws Exception { 727 NotificationChannel channel = 728 new NotificationChannel("id2", "name2", IMPORTANCE_LOW); 729 NotificationChannel channel2 = 730 new NotificationChannel("id4", "a", NotificationManager.IMPORTANCE_HIGH); 731 NotificationChannel channel3 = 732 new NotificationChannel("id4", "a", NotificationManager.IMPORTANCE_HIGH); 733 mHelper.createNotificationChannel(pkg, uid, channel, true); 734 mHelper.createNotificationChannel(pkg, uid, channel2, true); 735 mHelper.createNotificationChannel(pkg, uid, channel3, true); 736 737 mHelper.deleteNotificationChannel(pkg, uid, channel.getId()); 738 mHelper.deleteNotificationChannel(pkg, uid, channel3.getId()); 739 740 assertEquals(2, mHelper.getDeletedChannelCount(pkg, uid)); 741 assertEquals(0, mHelper.getDeletedChannelCount(pkg2, uid2)); 742 } 743 744 @Test 745 public void testUpdateDeletedChannels() throws Exception { 746 NotificationChannel channel = 747 new NotificationChannel("id2", "name2", IMPORTANCE_LOW); 748 mHelper.createNotificationChannel(pkg, uid, channel, true); 749 750 mHelper.deleteNotificationChannel(pkg, uid, channel.getId()); 751 752 channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes); 753 try { 754 mHelper.updateNotificationChannel(pkg, uid, channel); 755 fail("Updated deleted channel"); 756 } catch (IllegalArgumentException e) { 757 // :) 758 } 759 760 try { 761 mHelper.updateNotificationChannelFromAssistant(pkg, uid, channel); 762 fail("Updated deleted channel"); 763 } catch (IllegalArgumentException e) { 764 // :) 765 } 766 } 767 768 @Test 769 public void testCreateDeletedChannel() throws Exception { 770 long[] vibration = new long[]{100, 67, 145, 156}; 771 NotificationChannel channel = 772 new NotificationChannel("id2", "name2", IMPORTANCE_LOW); 773 channel.setVibrationPattern(vibration); 774 775 mHelper.createNotificationChannel(pkg, uid, channel, true); 776 mHelper.deleteNotificationChannel(pkg, uid, channel.getId()); 777 778 NotificationChannel newChannel = new NotificationChannel( 779 channel.getId(), channel.getName(), NotificationManager.IMPORTANCE_HIGH); 780 newChannel.setVibrationPattern(new long[]{100}); 781 782 mHelper.createNotificationChannel(pkg, uid, newChannel, true); 783 784 // No long deleted, using old settings 785 compareChannels(channel, 786 mHelper.getNotificationChannel(pkg, uid, newChannel.getId(), false)); 787 } 788 789 @Test 790 public void testCreateChannel_defaultChannelId() throws Exception { 791 try { 792 mHelper.createNotificationChannel(pkg2, uid2, new NotificationChannel( 793 NotificationChannel.DEFAULT_CHANNEL_ID, "ha", IMPORTANCE_HIGH), true); 794 fail("Allowed to create default channel"); 795 } catch (IllegalArgumentException e) { 796 // pass 797 } 798 } 799 800 @Test 801 public void testCreateChannel_alreadyExists() throws Exception { 802 long[] vibration = new long[]{100, 67, 145, 156}; 803 NotificationChannel channel = 804 new NotificationChannel("id2", "name2", IMPORTANCE_LOW); 805 channel.setVibrationPattern(vibration); 806 807 mHelper.createNotificationChannel(pkg, uid, channel, true); 808 809 NotificationChannel newChannel = new NotificationChannel( 810 channel.getId(), channel.getName(), NotificationManager.IMPORTANCE_HIGH); 811 newChannel.setVibrationPattern(new long[]{100}); 812 813 mHelper.createNotificationChannel(pkg, uid, newChannel, true); 814 815 // Old settings not overridden 816 compareChannels(channel, 817 mHelper.getNotificationChannel(pkg, uid, newChannel.getId(), false)); 818 } 819 820 @Test 821 public void testCreateChannel_addMissingSound() throws Exception { 822 final NotificationChannel channel = 823 new NotificationChannel("id2", "name2", IMPORTANCE_LOW); 824 mHelper.createNotificationChannel(pkg, uid, channel, true); 825 assertNotNull(mHelper.getNotificationChannel( 826 pkg, uid, channel.getId(), false).getSound()); 827 } 828 829 @Test 830 public void testCreateChannel_noOverrideSound() throws Exception { 831 Uri sound = new Uri.Builder().scheme("test").build(); 832 final NotificationChannel channel = new NotificationChannel("id2", "name2", 833 NotificationManager.IMPORTANCE_DEFAULT); 834 channel.setSound(sound, mAudioAttributes); 835 mHelper.createNotificationChannel(pkg, uid, channel, true); 836 assertEquals(sound, mHelper.getNotificationChannel( 837 pkg, uid, channel.getId(), false).getSound()); 838 } 839 840 @Test 841 public void testPermanentlyDeleteChannels() throws Exception { 842 NotificationChannel channel1 = 843 new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH); 844 NotificationChannel channel2 = 845 new NotificationChannel("id2", "name2", IMPORTANCE_LOW); 846 847 mHelper.createNotificationChannel(pkg, uid, channel1, true); 848 mHelper.createNotificationChannel(pkg, uid, channel2, false); 849 850 mHelper.permanentlyDeleteNotificationChannels(pkg, uid); 851 852 // Only default channel remains 853 assertEquals(1, mHelper.getNotificationChannels(pkg, uid, true).getList().size()); 854 } 855 856 @Test 857 public void testOnPackageChanged_packageRemoval() throws Exception { 858 // Deleted 859 NotificationChannel channel1 = 860 new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH); 861 mHelper.createNotificationChannel(pkg, uid, channel1, true); 862 863 mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{pkg}, new int[]{uid}); 864 865 assertEquals(0, mHelper.getNotificationChannels(pkg, uid, true).getList().size()); 866 867 // Not deleted 868 mHelper.createNotificationChannel(pkg, uid, channel1, true); 869 870 mHelper.onPackagesChanged(false, UserHandle.USER_SYSTEM, new String[]{pkg}, new int[]{uid}); 871 assertEquals(2, mHelper.getNotificationChannels(pkg, uid, false).getList().size()); 872 } 873 874 @Test 875 public void testOnPackageChanged_packageRemoval_importance() throws Exception { 876 mHelper.setImportance(pkg, uid, NotificationManager.IMPORTANCE_HIGH); 877 878 mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{pkg}, new int[]{uid}); 879 880 assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(pkg, uid)); 881 } 882 883 @Test 884 public void testOnPackageChanged_packageRemoval_groups() throws Exception { 885 NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1"); 886 mHelper.createNotificationChannelGroup(pkg, uid, ncg, true); 887 NotificationChannelGroup ncg2 = new NotificationChannelGroup("group2", "name2"); 888 mHelper.createNotificationChannelGroup(pkg, uid, ncg2, true); 889 890 mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{pkg}, new int[]{uid}); 891 892 assertEquals(0, mHelper.getNotificationChannelGroups(pkg, uid, true).getList().size()); 893 } 894 895 @Test 896 public void testRecordDefaults() throws Exception { 897 assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(pkg, uid)); 898 assertEquals(true, mHelper.canShowBadge(pkg, uid)); 899 assertEquals(1, mHelper.getNotificationChannels(pkg, uid, false).getList().size()); 900 } 901 902 @Test 903 public void testCreateGroup() throws Exception { 904 NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1"); 905 mHelper.createNotificationChannelGroup(pkg, uid, ncg, true); 906 assertEquals(ncg, mHelper.getNotificationChannelGroups(pkg, uid).iterator().next()); 907 } 908 909 @Test 910 public void testCannotCreateChannel_badGroup() throws Exception { 911 NotificationChannel channel1 = 912 new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH); 913 channel1.setGroup("garbage"); 914 try { 915 mHelper.createNotificationChannel(pkg, uid, channel1, true); 916 fail("Created a channel with a bad group"); 917 } catch (IllegalArgumentException e) { 918 } 919 } 920 921 @Test 922 public void testCannotCreateChannel_goodGroup() throws Exception { 923 NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1"); 924 mHelper.createNotificationChannelGroup(pkg, uid, ncg, true); 925 NotificationChannel channel1 = 926 new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH); 927 channel1.setGroup(ncg.getId()); 928 mHelper.createNotificationChannel(pkg, uid, channel1, true); 929 930 assertEquals(ncg.getId(), 931 mHelper.getNotificationChannel(pkg, uid, channel1.getId(), false).getGroup()); 932 } 933 934 @Test 935 public void testGetChannelGroups() throws Exception { 936 NotificationChannelGroup unused = new NotificationChannelGroup("unused", "s"); 937 mHelper.createNotificationChannelGroup(pkg, uid, unused, true); 938 NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1"); 939 mHelper.createNotificationChannelGroup(pkg, uid, ncg, true); 940 NotificationChannelGroup ncg2 = new NotificationChannelGroup("group2", "name2"); 941 mHelper.createNotificationChannelGroup(pkg, uid, ncg2, true); 942 943 NotificationChannel channel1 = 944 new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH); 945 channel1.setGroup(ncg.getId()); 946 mHelper.createNotificationChannel(pkg, uid, channel1, true); 947 NotificationChannel channel1a = 948 new NotificationChannel("id1a", "name1", NotificationManager.IMPORTANCE_HIGH); 949 channel1a.setGroup(ncg.getId()); 950 mHelper.createNotificationChannel(pkg, uid, channel1a, true); 951 952 NotificationChannel channel2 = 953 new NotificationChannel("id2", "name1", NotificationManager.IMPORTANCE_HIGH); 954 channel2.setGroup(ncg2.getId()); 955 mHelper.createNotificationChannel(pkg, uid, channel2, true); 956 957 NotificationChannel channel3 = 958 new NotificationChannel("id3", "name1", NotificationManager.IMPORTANCE_HIGH); 959 mHelper.createNotificationChannel(pkg, uid, channel3, true); 960 961 List<NotificationChannelGroup> actual = 962 mHelper.getNotificationChannelGroups(pkg, uid, true).getList(); 963 assertEquals(3, actual.size()); 964 for (NotificationChannelGroup group : actual) { 965 if (group.getId() == null) { 966 assertEquals(2, group.getChannels().size()); // misc channel too 967 assertTrue(channel3.getId().equals(group.getChannels().get(0).getId()) 968 || channel3.getId().equals(group.getChannels().get(1).getId())); 969 } else if (group.getId().equals(ncg.getId())) { 970 assertEquals(2, group.getChannels().size()); 971 if (group.getChannels().get(0).getId().equals(channel1.getId())) { 972 assertTrue(group.getChannels().get(1).getId().equals(channel1a.getId())); 973 } else if (group.getChannels().get(0).getId().equals(channel1a.getId())) { 974 assertTrue(group.getChannels().get(1).getId().equals(channel1.getId())); 975 } else { 976 fail("expected channel not found"); 977 } 978 } else if (group.getId().equals(ncg2.getId())) { 979 assertEquals(1, group.getChannels().size()); 980 assertEquals(channel2.getId(), group.getChannels().get(0).getId()); 981 } 982 } 983 } 984 985 @Test 986 public void testGetChannelGroups_noSideEffects() throws Exception { 987 NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1"); 988 mHelper.createNotificationChannelGroup(pkg, uid, ncg, true); 989 990 NotificationChannel channel1 = 991 new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH); 992 channel1.setGroup(ncg.getId()); 993 mHelper.createNotificationChannel(pkg, uid, channel1, true); 994 mHelper.getNotificationChannelGroups(pkg, uid, true).getList(); 995 996 channel1.setImportance(IMPORTANCE_LOW); 997 mHelper.updateNotificationChannel(pkg, uid, channel1); 998 999 List<NotificationChannelGroup> actual = 1000 mHelper.getNotificationChannelGroups(pkg, uid, true).getList(); 1001 1002 assertEquals(2, actual.size()); 1003 for (NotificationChannelGroup group : actual) { 1004 if (Objects.equals(group.getId(), ncg.getId())) { 1005 assertEquals(1, group.getChannels().size()); 1006 } 1007 } 1008 } 1009 1010 @Test 1011 public void testCreateChannel_updateName() throws Exception { 1012 NotificationChannel nc = new NotificationChannel("id", "hello", IMPORTANCE_DEFAULT); 1013 mHelper.createNotificationChannel(pkg, uid, nc, true); 1014 NotificationChannel actual = mHelper.getNotificationChannel(pkg, uid, "id", false); 1015 assertEquals("hello", actual.getName()); 1016 1017 nc = new NotificationChannel("id", "goodbye", IMPORTANCE_HIGH); 1018 mHelper.createNotificationChannel(pkg, uid, nc, true); 1019 1020 actual = mHelper.getNotificationChannel(pkg, uid, "id", false); 1021 assertEquals("goodbye", actual.getName()); 1022 assertEquals(IMPORTANCE_DEFAULT, actual.getImportance()); 1023 } 1024 1025 @Test 1026 public void testDumpChannelsJson() throws Exception { 1027 final ApplicationInfo upgrade = new ApplicationInfo(); 1028 upgrade.targetSdkVersion = Build.VERSION_CODES.O; 1029 try { 1030 when(mPm.getApplicationInfoAsUser( 1031 anyString(), anyInt(), anyInt())).thenReturn(upgrade); 1032 } catch (PackageManager.NameNotFoundException e) { 1033 } 1034 ArrayMap<String, Integer> expectedChannels = new ArrayMap<>(); 1035 int numPackages = ThreadLocalRandom.current().nextInt(1, 5); 1036 for (int i = 0; i < numPackages; i++) { 1037 String pkgName = "pkg" + i; 1038 int numChannels = ThreadLocalRandom.current().nextInt(1, 10); 1039 for (int j = 0; j < numChannels; j++) { 1040 mHelper.createNotificationChannel(pkgName, uid, 1041 new NotificationChannel("" + j, "a", IMPORTANCE_HIGH), true); 1042 } 1043 expectedChannels.put(pkgName, numChannels); 1044 } 1045 1046 // delete the first channel of the first package 1047 String pkg = expectedChannels.keyAt(0); 1048 mHelper.deleteNotificationChannel("pkg" + 0, uid, "0"); 1049 // dump should not include deleted channels 1050 int count = expectedChannels.get(pkg); 1051 expectedChannels.put(pkg, count - 1); 1052 1053 JSONArray actual = mHelper.dumpChannelsJson(new NotificationManagerService.DumpFilter()); 1054 assertEquals(numPackages, actual.length()); 1055 for (int i = 0; i < numPackages; i++) { 1056 JSONObject object = actual.getJSONObject(i); 1057 assertTrue(expectedChannels.containsKey(object.get("packageName"))); 1058 assertEquals(expectedChannels.get(object.get("packageName")).intValue() + 1, 1059 object.getInt("channelCount")); 1060 } 1061 } 1062} 1063