1 /* 2 * Copyright (C) 2008 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 17 package android.app.notification.current.cts; 18 19 import static android.Manifest.permission.POST_NOTIFICATIONS; 20 import static android.Manifest.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL; 21 import static android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS; 22 import static android.app.Activity.RESULT_OK; 23 import static android.app.AppOpsManager.MODE_ALLOWED; 24 import static android.app.AppOpsManager.MODE_DEFAULT; 25 import static android.app.AppOpsManager.MODE_ERRORED; 26 import static android.app.Notification.FLAG_FOREGROUND_SERVICE; 27 import static android.app.Notification.FLAG_USER_INITIATED_JOB; 28 import static android.app.NotificationManager.IMPORTANCE_DEFAULT; 29 import static android.app.NotificationManager.IMPORTANCE_HIGH; 30 import static android.app.NotificationManager.IMPORTANCE_LOW; 31 import static android.app.NotificationManager.IMPORTANCE_MIN; 32 import static android.app.NotificationManager.IMPORTANCE_NONE; 33 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; 34 import static android.content.pm.PackageManager.FEATURE_WATCH; 35 import static android.service.notification.NotificationListenerService.META_DATA_DEFAULT_AUTOBIND; 36 37 import static com.google.common.truth.Truth.assertThat; 38 39 import static org.hamcrest.CoreMatchers.hasItem; 40 import static org.junit.Assert.assertThat; 41 42 import android.Manifest; 43 import android.app.Notification; 44 import android.app.NotificationChannel; 45 import android.app.NotificationChannelGroup; 46 import android.app.NotificationManager; 47 import android.app.PendingIntent; 48 import android.app.role.RoleManager; 49 import android.app.stubs.GetResultActivity; 50 import android.app.stubs.R; 51 import android.app.stubs.shared.FutureServiceConnection; 52 import android.app.stubs.shared.NotificationHelper.SEARCH_TYPE; 53 import android.app.stubs.shared.TestNotificationListener; 54 import android.content.ComponentName; 55 import android.content.ContentResolver; 56 import android.content.Context; 57 import android.content.Intent; 58 import android.content.ServiceConnection; 59 import android.content.pm.PackageInfo; 60 import android.content.pm.PackageManager; 61 import android.content.pm.ServiceInfo; 62 import android.content.res.AssetFileDescriptor; 63 import android.graphics.Bitmap; 64 import android.graphics.drawable.Icon; 65 import android.media.AudioAttributes; 66 import android.media.session.MediaSession; 67 import android.net.Uri; 68 import android.os.Build; 69 import android.os.Handler; 70 import android.os.IBinder; 71 import android.os.Looper; 72 import android.os.Message; 73 import android.os.Messenger; 74 import android.os.UserHandle; 75 import android.permission.PermissionManager; 76 import android.permission.cts.PermissionUtils; 77 import android.platform.test.annotations.AsbSecurityTest; 78 import android.provider.Settings; 79 import android.service.notification.NotificationListenerService; 80 import android.service.notification.StatusBarNotification; 81 import android.util.ArrayMap; 82 import android.util.Log; 83 import android.widget.RemoteViews; 84 85 import androidx.annotation.NonNull; 86 import androidx.annotation.Nullable; 87 import androidx.test.platform.app.InstrumentationRegistry; 88 import androidx.test.uiautomator.UiDevice; 89 90 import com.android.compatibility.common.util.PollingCheck; 91 import com.android.compatibility.common.util.SystemUtil; 92 import com.android.compatibility.common.util.ThrowingSupplier; 93 import com.android.test.notificationlistener.INLSControlService; 94 import com.android.test.notificationlistener.INotificationUriAccessService; 95 96 import com.google.common.base.Preconditions; 97 98 import java.io.BufferedReader; 99 import java.io.IOException; 100 import java.io.InputStreamReader; 101 import java.util.ArrayList; 102 import java.util.Arrays; 103 import java.util.Collections; 104 import java.util.HashMap; 105 import java.util.List; 106 import java.util.Map; 107 import java.util.UUID; 108 import java.util.concurrent.CompletableFuture; 109 import java.util.concurrent.CompletionException; 110 import java.util.concurrent.CountDownLatch; 111 import java.util.concurrent.ExecutionException; 112 import java.util.concurrent.Executor; 113 import java.util.concurrent.Semaphore; 114 import java.util.concurrent.TimeUnit; 115 import java.util.concurrent.TimeoutException; 116 117 /* This tests NotificationListenerService together with NotificationManager, as you need to have 118 * notifications to manipulate in order to test the listener service. */ 119 public class NotificationManagerTest extends BaseNotificationManagerTest { 120 public static final String NOTIFICATIONPROVIDER = "com.android.test.notificationprovider"; 121 public static final String RICH_NOTIFICATION_ACTIVITY = 122 "com.android.test.notificationprovider.RichNotificationActivity"; 123 final String TAG = NotificationManagerTest.class.getSimpleName(); 124 final boolean DEBUG = false; 125 126 private static final String DELEGATE_POST_CLASS = TEST_APP + ".NotificationDelegateAndPost"; 127 private static final String REVOKE_CLASS = TEST_APP + ".NotificationRevoker"; 128 129 private static final String TRAMPOLINE_APP = 130 "com.android.test.notificationtrampoline.current"; 131 private static final String TRAMPOLINE_APP_API_30 = 132 "com.android.test.notificationtrampoline.api30"; 133 private static final String TRAMPOLINE_APP_API_32 = 134 "com.android.test.notificationtrampoline.api32"; 135 private static final ComponentName TRAMPOLINE_SERVICE = 136 new ComponentName(TRAMPOLINE_APP, 137 "com.android.test.notificationtrampoline.NotificationTrampolineTestService"); 138 private static final ComponentName TRAMPOLINE_SERVICE_API_30 = 139 new ComponentName(TRAMPOLINE_APP_API_30, 140 "com.android.test.notificationtrampoline.NotificationTrampolineTestService"); 141 private static final ComponentName TRAMPOLINE_SERVICE_API_32 = 142 new ComponentName(TRAMPOLINE_APP_API_32, 143 "com.android.test.notificationtrampoline.NotificationTrampolineTestService"); 144 145 private static final ComponentName URI_ACCESS_SERVICE = new ComponentName( 146 "com.android.test.notificationlistener", 147 "com.android.test.notificationlistener.NotificationUriAccessService"); 148 149 private static final ComponentName NLS_CONTROL_SERVICE = new ComponentName( 150 "com.android.test.notificationlistener", 151 "com.android.test.notificationlistener.NLSControlService"); 152 153 private static final ComponentName NO_AUTOBIND_NLS = new ComponentName( 154 "com.android.test.notificationlistener", 155 "com.android.test.notificationlistener.TestNotificationListenerNoAutobind"); 156 157 private static final String STUB_PACKAGE_NAME = "android.app.stubs"; 158 159 private static final long TIMEOUT_LONG_MS = 10000; 160 private static final long TIMEOUT_MS = 4000; 161 162 private static final long POST_TIMEOUT = 200; 163 private static final int MESSAGE_BROADCAST_NOTIFICATION = 1; 164 private static final int MESSAGE_SERVICE_NOTIFICATION = 2; 165 private static final int MESSAGE_CLICK_NOTIFICATION = 3; 166 167 private String mId; 168 private INotificationUriAccessService mNotificationUriAccessService; 169 private INLSControlService mNLSControlService; 170 private FutureServiceConnection mTrampolineConnection; 171 172 @Nullable 173 private List<String> mPreviousDefaultBrowser; 174 175 @Override setUp()176 protected void setUp() throws Exception { 177 super.setUp(); 178 PermissionUtils.grantPermission(mContext.getPackageName(), POST_NOTIFICATIONS); 179 PermissionUtils.grantPermission(STUB_PACKAGE_NAME, POST_NOTIFICATIONS); 180 PermissionUtils.grantPermission(TEST_APP, POST_NOTIFICATIONS); 181 PermissionUtils.grantPermission(TRAMPOLINE_APP, POST_NOTIFICATIONS); 182 PermissionUtils.grantPermission(TRAMPOLINE_APP_API_30, POST_NOTIFICATIONS); 183 PermissionUtils.grantPermission(TRAMPOLINE_APP_API_32, POST_NOTIFICATIONS); 184 PermissionUtils.grantPermission(NOTIFICATIONPROVIDER, POST_NOTIFICATIONS); 185 186 // This will leave a set of channels on the device with each test run. 187 mId = UUID.randomUUID().toString(); 188 189 // delay between tests so notifications aren't dropped by the rate limiter 190 try { 191 Thread.sleep(500); 192 } catch (InterruptedException e) { 193 } 194 } 195 196 @Override tearDown()197 protected void tearDown() throws Exception { 198 super.tearDown(); 199 200 // For trampoline tests 201 if (mTrampolineConnection != null) { 202 mContext.unbindService(mTrampolineConnection); 203 mTrampolineConnection = null; 204 } 205 if (mListener != null) { 206 mListener.removeTestPackage(TRAMPOLINE_APP_API_30); 207 mListener.removeTestPackage(TRAMPOLINE_APP); 208 } 209 if (mPreviousDefaultBrowser != null) { 210 restoreDefaultBrowser(); 211 } 212 213 // Use test API to prevent PermissionManager from killing the test process when revoking 214 // permission. 215 SystemUtil.runWithShellPermissionIdentity( 216 () -> mContext.getSystemService(PermissionManager.class) 217 .revokePostNotificationPermissionWithoutKillForTest( 218 mContext.getPackageName(), 219 android.os.Process.myUserHandle().getIdentifier()), 220 REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL, 221 REVOKE_RUNTIME_PERMISSIONS); 222 PermissionUtils.revokePermission(STUB_PACKAGE_NAME, POST_NOTIFICATIONS); 223 PermissionUtils.revokePermission(TEST_APP, POST_NOTIFICATIONS); 224 PermissionUtils.revokePermission(TRAMPOLINE_APP, POST_NOTIFICATIONS); 225 PermissionUtils.revokePermission(NOTIFICATIONPROVIDER, POST_NOTIFICATIONS); 226 } 227 getPendingIntent()228 private PendingIntent getPendingIntent() { 229 return PendingIntent.getActivity( 230 getContext(), 0, new Intent(getContext(), this.getClass()), 231 PendingIntent.FLAG_MUTABLE_UNAUDITED); 232 } 233 isGroupSummary(Notification n)234 private boolean isGroupSummary(Notification n) { 235 return n.getGroup() != null && (n.flags & Notification.FLAG_GROUP_SUMMARY) != 0; 236 } 237 assertOnlySomeNotificationsAutogrouped(List<Integer> autoGroupedIds)238 private void assertOnlySomeNotificationsAutogrouped(List<Integer> autoGroupedIds) { 239 String expectedGroupKey = null; 240 try { 241 // Posting can take ~100 ms 242 Thread.sleep(150); 243 } catch (InterruptedException e) { 244 e.printStackTrace(); 245 } 246 StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications(); 247 for (StatusBarNotification sbn : sbns) { 248 if (isGroupSummary(sbn.getNotification()) 249 || autoGroupedIds.contains(sbn.getId())) { 250 assertTrue(sbn.getKey() + " is unexpectedly not autogrouped", 251 sbn.getOverrideGroupKey() != null); 252 if (expectedGroupKey == null) { 253 expectedGroupKey = sbn.getGroupKey(); 254 } 255 assertEquals(expectedGroupKey, sbn.getGroupKey()); 256 } else { 257 assertTrue(sbn.isGroup()); 258 assertTrue(sbn.getKey() + " is unexpectedly autogrouped,", 259 sbn.getOverrideGroupKey() == null); 260 assertTrue(sbn.getKey() + " has an unusual group key", 261 sbn.getGroupKey() != expectedGroupKey); 262 } 263 } 264 } 265 assertAllPostedNotificationsAutogrouped()266 private void assertAllPostedNotificationsAutogrouped() { 267 String expectedGroupKey = null; 268 try { 269 // Posting can take ~100 ms 270 Thread.sleep(150); 271 } catch (InterruptedException e) { 272 e.printStackTrace(); 273 } 274 StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications(); 275 for (StatusBarNotification sbn : sbns) { 276 // all notis should be in a group determined by autogrouping 277 assertTrue(sbn.getOverrideGroupKey() != null); 278 if (expectedGroupKey == null) { 279 expectedGroupKey = sbn.getGroupKey(); 280 } 281 // all notis should be in the same group 282 assertEquals(expectedGroupKey, sbn.getGroupKey()); 283 } 284 } 285 getCancellationReason(String key)286 private int getCancellationReason(String key) { 287 for (int tries = 3; tries-- > 0; ) { 288 if (mListener.mRemoved.containsKey(key)) { 289 return mListener.mRemoved.get(key); 290 } 291 try { 292 Thread.sleep(1000); 293 } catch (InterruptedException ex) { 294 // pass 295 } 296 } 297 return -1; 298 } 299 getAssistantCancellationReason(String key)300 private int getAssistantCancellationReason(String key) { 301 for (int tries = 3; tries-- > 0; ) { 302 if (mAssistant.mRemoved.containsKey(key)) { 303 return mAssistant.mRemoved.get(key); 304 } 305 try { 306 Thread.sleep(1000); 307 } catch (InterruptedException ex) { 308 // pass 309 } 310 } 311 return -1; 312 } 313 assertNotificationCount(int expectedCount)314 private void assertNotificationCount(int expectedCount) { 315 // notification is a bit asynchronous so it may take a few ms to appear in 316 // getActiveNotifications() 317 // we will check for it for up to 400ms before giving up 318 int lastCount = 0; 319 for (int tries = 4; tries-- > 0; ) { 320 final StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications(); 321 lastCount = sbns.length; 322 if (expectedCount == lastCount) return; 323 try { 324 Thread.sleep(100); 325 } catch (InterruptedException ex) { 326 // pass 327 } 328 } 329 fail("Expected " + expectedCount + " posted notifications, were " + lastCount); 330 } 331 compareChannels(NotificationChannel expected, NotificationChannel actual)332 private void compareChannels(NotificationChannel expected, NotificationChannel actual) { 333 if (actual == null) { 334 fail("actual channel is null"); 335 return; 336 } 337 if (expected == null) { 338 fail("expected channel is null"); 339 return; 340 } 341 assertEquals(expected.getId(), actual.getId()); 342 assertEquals(expected.getName(), actual.getName()); 343 assertEquals(expected.getDescription(), actual.getDescription()); 344 assertEquals(expected.shouldVibrate(), actual.shouldVibrate()); 345 assertEquals(expected.shouldShowLights(), actual.shouldShowLights()); 346 assertEquals(expected.getLightColor(), actual.getLightColor()); 347 assertEquals(expected.getImportance(), actual.getImportance()); 348 if (expected.getSound() == null) { 349 assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actual.getSound()); 350 assertEquals(Notification.AUDIO_ATTRIBUTES_DEFAULT, actual.getAudioAttributes()); 351 } else { 352 assertEquals(expected.getSound(), actual.getSound()); 353 assertEquals(expected.getAudioAttributes(), actual.getAudioAttributes()); 354 } 355 assertTrue(Arrays.equals(expected.getVibrationPattern(), actual.getVibrationPattern())); 356 assertEquals(expected.getGroup(), actual.getGroup()); 357 assertEquals(expected.getConversationId(), actual.getConversationId()); 358 assertEquals(expected.getParentChannelId(), actual.getParentChannelId()); 359 assertEquals(expected.isDemoted(), actual.isDemoted()); 360 } 361 sendTrampolineMessage(ComponentName component, int message, int notificationId, Handler callback)362 private void sendTrampolineMessage(ComponentName component, int message, 363 int notificationId, Handler callback) throws Exception { 364 if (mTrampolineConnection == null) { 365 Intent intent = new Intent(); 366 intent.setComponent(component); 367 mTrampolineConnection = new FutureServiceConnection(); 368 assertTrue( 369 mContext.bindService(intent, mTrampolineConnection, Context.BIND_AUTO_CREATE)); 370 } 371 Messenger service = new Messenger(mTrampolineConnection.get(TIMEOUT_MS)); 372 service.send(Message.obtain(null, message, notificationId, -1, new Messenger(callback))); 373 } 374 setDefaultBrowser(String packageName)375 private void setDefaultBrowser(String packageName) throws Exception { 376 UserHandle user = android.os.Process.myUserHandle(); 377 mPreviousDefaultBrowser = SystemUtil.callWithShellPermissionIdentity( 378 () -> mRoleManager.getRoleHoldersAsUser(RoleManager.ROLE_BROWSER, user)); 379 CompletableFuture<Boolean> set = new CompletableFuture<>(); 380 SystemUtil.runWithShellPermissionIdentity( 381 () -> mRoleManager.addRoleHolderAsUser(RoleManager.ROLE_BROWSER, packageName, 0, 382 user, mContext.getMainExecutor(), set::complete)); 383 assertTrue("Failed to set " + packageName + " as default browser", 384 set.get(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 385 } 386 restoreDefaultBrowser()387 private void restoreDefaultBrowser() throws Exception { 388 Preconditions.checkState(mPreviousDefaultBrowser != null); 389 UserHandle user = android.os.Process.myUserHandle(); 390 Executor executor = mContext.getMainExecutor(); 391 CompletableFuture<Boolean> restored = new CompletableFuture<>(); 392 SystemUtil.runWithShellPermissionIdentity(() -> { 393 mRoleManager.clearRoleHoldersAsUser(RoleManager.ROLE_BROWSER, 0, user, executor, 394 restored::complete); 395 for (String packageName : mPreviousDefaultBrowser) { 396 mRoleManager.addRoleHolderAsUser(RoleManager.ROLE_BROWSER, packageName, 397 0, user, executor, restored::complete); 398 } 399 }); 400 assertTrue("Failed to restore default browser", 401 restored.get(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 402 } 403 404 /** 405 * Previous tests could have started activities within the grace period, so go home to avoid 406 * allowing background activity starts due to this exemption. 407 */ deactivateGracePeriod()408 private void deactivateGracePeriod() { 409 UiDevice.getInstance(mInstrumentation).pressHome(); 410 } 411 verifyCanUseFullScreenIntent(int appOpState, boolean canSend)412 private void verifyCanUseFullScreenIntent(int appOpState, boolean canSend) throws Exception { 413 final int previousState = PermissionUtils.getAppOp(STUB_PACKAGE_NAME, 414 Manifest.permission.USE_FULL_SCREEN_INTENT); 415 try { 416 PermissionUtils.setAppOp(STUB_PACKAGE_NAME, 417 Manifest.permission.USE_FULL_SCREEN_INTENT, 418 appOpState); 419 420 if (canSend) { 421 assertTrue(mNotificationManager.canUseFullScreenIntent()); 422 } else { 423 assertFalse(mNotificationManager.canUseFullScreenIntent()); 424 } 425 426 } finally { 427 // Clean up by setting to app op to previous state. 428 PermissionUtils.setAppOp(STUB_PACKAGE_NAME, 429 Manifest.permission.USE_FULL_SCREEN_INTENT, 430 previousState); 431 } 432 } 433 testCanSendFullScreenIntent_modeDefault_returnsIsPermissionGranted()434 public void testCanSendFullScreenIntent_modeDefault_returnsIsPermissionGranted() 435 throws Exception { 436 final boolean isPermissionGranted = PermissionUtils.isPermissionGranted(STUB_PACKAGE_NAME, 437 Manifest.permission.USE_FULL_SCREEN_INTENT); 438 verifyCanUseFullScreenIntent(MODE_DEFAULT, /*canSend=*/ isPermissionGranted); 439 } 440 testCanSendFullScreenIntent_modeAllowed_returnsTrue()441 public void testCanSendFullScreenIntent_modeAllowed_returnsTrue() throws Exception { 442 verifyCanUseFullScreenIntent(MODE_ALLOWED, /*canSend=*/ true); 443 } 444 testCanSendFullScreenIntent_modeErrored_returnsFalse()445 public void testCanSendFullScreenIntent_modeErrored_returnsFalse() throws Exception { 446 verifyCanUseFullScreenIntent(MODE_ERRORED, /*canSend=*/ false); 447 } 448 testCreateChannelGroup()449 public void testCreateChannelGroup() throws Exception { 450 final NotificationChannelGroup ncg = new NotificationChannelGroup("a group", "a label"); 451 final NotificationChannel channel = 452 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 453 channel.setGroup(ncg.getId()); 454 mNotificationManager.createNotificationChannelGroup(ncg); 455 final NotificationChannel ungrouped = 456 new NotificationChannel(mId + "!", "name", IMPORTANCE_DEFAULT); 457 try { 458 mNotificationManager.createNotificationChannel(channel); 459 mNotificationManager.createNotificationChannel(ungrouped); 460 461 List<NotificationChannelGroup> ncgs = 462 mNotificationManager.getNotificationChannelGroups(); 463 assertEquals(1, ncgs.size()); 464 assertEquals(ncg.getName(), ncgs.get(0).getName()); 465 assertEquals(ncg.getDescription(), ncgs.get(0).getDescription()); 466 assertEquals(channel.getId(), ncgs.get(0).getChannels().get(0).getId()); 467 } finally { 468 mNotificationManager.deleteNotificationChannelGroup(ncg.getId()); 469 } 470 } 471 testGetChannelGroup()472 public void testGetChannelGroup() throws Exception { 473 final NotificationChannelGroup ncg = new NotificationChannelGroup("a group", "a label"); 474 ncg.setDescription("bananas"); 475 final NotificationChannelGroup ncg2 = new NotificationChannelGroup("group 2", "label 2"); 476 final NotificationChannel channel = 477 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 478 channel.setGroup(ncg.getId()); 479 480 mNotificationManager.createNotificationChannelGroup(ncg); 481 mNotificationManager.createNotificationChannelGroup(ncg2); 482 mNotificationManager.createNotificationChannel(channel); 483 484 NotificationChannelGroup actual = 485 mNotificationManager.getNotificationChannelGroup(ncg.getId()); 486 assertEquals(ncg.getId(), actual.getId()); 487 assertEquals(ncg.getName(), actual.getName()); 488 assertEquals(ncg.getDescription(), actual.getDescription()); 489 assertEquals(channel.getId(), actual.getChannels().get(0).getId()); 490 } 491 testGetChannelGroups()492 public void testGetChannelGroups() throws Exception { 493 final NotificationChannelGroup ncg = new NotificationChannelGroup("a group", "a label"); 494 ncg.setDescription("bananas"); 495 final NotificationChannelGroup ncg2 = new NotificationChannelGroup("group 2", "label 2"); 496 final NotificationChannel channel = 497 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 498 channel.setGroup(ncg2.getId()); 499 500 mNotificationManager.createNotificationChannelGroup(ncg); 501 mNotificationManager.createNotificationChannelGroup(ncg2); 502 mNotificationManager.createNotificationChannel(channel); 503 504 List<NotificationChannelGroup> actual = 505 mNotificationManager.getNotificationChannelGroups(); 506 assertEquals(2, actual.size()); 507 for (NotificationChannelGroup group : actual) { 508 if (group.getId().equals(ncg.getId())) { 509 assertEquals(group.getName(), ncg.getName()); 510 assertEquals(group.getDescription(), ncg.getDescription()); 511 assertEquals(0, group.getChannels().size()); 512 } else if (group.getId().equals(ncg2.getId())) { 513 assertEquals(group.getName(), ncg2.getName()); 514 assertEquals(group.getDescription(), ncg2.getDescription()); 515 assertEquals(1, group.getChannels().size()); 516 assertEquals(channel.getId(), group.getChannels().get(0).getId()); 517 } else { 518 fail("Extra group found " + group.getId()); 519 } 520 } 521 } 522 testDeleteChannelGroup()523 public void testDeleteChannelGroup() throws Exception { 524 final NotificationChannelGroup ncg = new NotificationChannelGroup("a group", "a label"); 525 final NotificationChannel channel = 526 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 527 channel.setGroup(ncg.getId()); 528 mNotificationManager.createNotificationChannelGroup(ncg); 529 mNotificationManager.createNotificationChannel(channel); 530 531 mNotificationManager.deleteNotificationChannelGroup(ncg.getId()); 532 533 assertNull(mNotificationManager.getNotificationChannel(channel.getId())); 534 assertEquals(0, mNotificationManager.getNotificationChannelGroups().size()); 535 } 536 testCreateChannel()537 public void testCreateChannel() throws Exception { 538 final NotificationChannel channel = 539 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 540 channel.setDescription("bananas"); 541 channel.enableVibration(true); 542 channel.setVibrationPattern(new long[]{5, 8, 2, 1}); 543 channel.setSound(new Uri.Builder().scheme("test").build(), 544 new AudioAttributes.Builder().setUsage( 545 AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED).build()); 546 channel.enableLights(true); 547 channel.setBypassDnd(true); 548 channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET); 549 mNotificationManager.createNotificationChannel(channel); 550 final NotificationChannel createdChannel = 551 mNotificationManager.getNotificationChannel(mId); 552 compareChannels(channel, createdChannel); 553 // Lockscreen Visibility and canBypassDnd no longer settable. 554 assertTrue(createdChannel.getLockscreenVisibility() != Notification.VISIBILITY_SECRET); 555 assertFalse(createdChannel.canBypassDnd()); 556 } 557 testCreateChannel_rename()558 public void testCreateChannel_rename() throws Exception { 559 NotificationChannel channel = 560 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 561 mNotificationManager.createNotificationChannel(channel); 562 channel.setName("new name"); 563 mNotificationManager.createNotificationChannel(channel); 564 final NotificationChannel createdChannel = 565 mNotificationManager.getNotificationChannel(mId); 566 compareChannels(channel, createdChannel); 567 568 channel.setImportance(NotificationManager.IMPORTANCE_HIGH); 569 mNotificationManager.createNotificationChannel(channel); 570 assertEquals(NotificationManager.IMPORTANCE_DEFAULT, 571 mNotificationManager.getNotificationChannel(mId).getImportance()); 572 } 573 testCreateChannel_addToGroup()574 public void testCreateChannel_addToGroup() throws Exception { 575 String oldGroup = null; 576 String newGroup = "new group"; 577 mNotificationManager.createNotificationChannelGroup( 578 new NotificationChannelGroup(newGroup, newGroup)); 579 580 NotificationChannel channel = 581 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 582 channel.setGroup(oldGroup); 583 mNotificationManager.createNotificationChannel(channel); 584 585 channel.setGroup(newGroup); 586 mNotificationManager.createNotificationChannel(channel); 587 588 final NotificationChannel updatedChannel = 589 mNotificationManager.getNotificationChannel(mId); 590 assertEquals("Failed to add non-grouped channel to a group on update ", 591 newGroup, updatedChannel.getGroup()); 592 } 593 testCreateChannel_cannotChangeGroup()594 public void testCreateChannel_cannotChangeGroup() throws Exception { 595 String oldGroup = "old group"; 596 String newGroup = "new group"; 597 mNotificationManager.createNotificationChannelGroup( 598 new NotificationChannelGroup(oldGroup, oldGroup)); 599 mNotificationManager.createNotificationChannelGroup( 600 new NotificationChannelGroup(newGroup, newGroup)); 601 602 NotificationChannel channel = 603 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 604 channel.setGroup(oldGroup); 605 mNotificationManager.createNotificationChannel(channel); 606 channel.setGroup(newGroup); 607 mNotificationManager.createNotificationChannel(channel); 608 final NotificationChannel updatedChannel = 609 mNotificationManager.getNotificationChannel(mId); 610 assertEquals("Channels should not be allowed to change groups", 611 oldGroup, updatedChannel.getGroup()); 612 } 613 testCreateSameChannelDoesNotUpdate()614 public void testCreateSameChannelDoesNotUpdate() throws Exception { 615 final NotificationChannel channel = 616 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 617 mNotificationManager.createNotificationChannel(channel); 618 final NotificationChannel channelDupe = 619 new NotificationChannel(mId, "name", IMPORTANCE_HIGH); 620 mNotificationManager.createNotificationChannel(channelDupe); 621 final NotificationChannel createdChannel = 622 mNotificationManager.getNotificationChannel(mId); 623 compareChannels(channel, createdChannel); 624 } 625 testCreateChannelAlreadyExistsNoOp()626 public void testCreateChannelAlreadyExistsNoOp() throws Exception { 627 NotificationChannel channel = 628 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 629 mNotificationManager.createNotificationChannel(channel); 630 NotificationChannel channelDupe = 631 new NotificationChannel(mId, "name", IMPORTANCE_HIGH); 632 mNotificationManager.createNotificationChannel(channelDupe); 633 compareChannels(channel, mNotificationManager.getNotificationChannel(channel.getId())); 634 } 635 testCreateChannelWithGroup()636 public void testCreateChannelWithGroup() throws Exception { 637 NotificationChannelGroup ncg = new NotificationChannelGroup("g", "n"); 638 mNotificationManager.createNotificationChannelGroup(ncg); 639 try { 640 NotificationChannel channel = 641 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 642 channel.setGroup(ncg.getId()); 643 mNotificationManager.createNotificationChannel(channel); 644 compareChannels(channel, mNotificationManager.getNotificationChannel(channel.getId())); 645 } finally { 646 mNotificationManager.deleteNotificationChannelGroup(ncg.getId()); 647 } 648 } 649 testCreateChannelWithBadGroup()650 public void testCreateChannelWithBadGroup() throws Exception { 651 NotificationChannel channel = 652 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 653 channel.setGroup("garbage"); 654 try { 655 mNotificationManager.createNotificationChannel(channel); 656 fail("Created notification with bad group"); 657 } catch (IllegalArgumentException e) { 658 } 659 } 660 testCreateChannelInvalidImportance()661 public void testCreateChannelInvalidImportance() throws Exception { 662 NotificationChannel channel = 663 new NotificationChannel(mId, "name", IMPORTANCE_UNSPECIFIED); 664 try { 665 mNotificationManager.createNotificationChannel(channel); 666 } catch (IllegalArgumentException e) { 667 //success 668 } 669 } 670 testDeleteChannel()671 public void testDeleteChannel() throws Exception { 672 NotificationChannel channel = 673 new NotificationChannel(mId, "name", IMPORTANCE_LOW); 674 mNotificationManager.createNotificationChannel(channel); 675 compareChannels(channel, mNotificationManager.getNotificationChannel(channel.getId())); 676 mNotificationManager.deleteNotificationChannel(channel.getId()); 677 assertNull(mNotificationManager.getNotificationChannel(channel.getId())); 678 } 679 testCannotDeleteDefaultChannel()680 public void testCannotDeleteDefaultChannel() throws Exception { 681 try { 682 mNotificationManager.deleteNotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID); 683 fail("Deleted default channel"); 684 } catch (IllegalArgumentException e) { 685 //success 686 } 687 } 688 testGetChannel()689 public void testGetChannel() throws Exception { 690 NotificationChannel channel1 = 691 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 692 NotificationChannel channel2 = 693 new NotificationChannel( 694 UUID.randomUUID().toString(), "name2", IMPORTANCE_HIGH); 695 NotificationChannel channel3 = 696 new NotificationChannel( 697 UUID.randomUUID().toString(), "name3", IMPORTANCE_LOW); 698 NotificationChannel channel4 = 699 new NotificationChannel( 700 UUID.randomUUID().toString(), "name4", IMPORTANCE_MIN); 701 mNotificationManager.createNotificationChannel(channel1); 702 mNotificationManager.createNotificationChannel(channel2); 703 mNotificationManager.createNotificationChannel(channel3); 704 mNotificationManager.createNotificationChannel(channel4); 705 706 compareChannels(channel2, 707 mNotificationManager.getNotificationChannel(channel2.getId())); 708 compareChannels(channel3, 709 mNotificationManager.getNotificationChannel(channel3.getId())); 710 compareChannels(channel1, 711 mNotificationManager.getNotificationChannel(channel1.getId())); 712 compareChannels(channel4, 713 mNotificationManager.getNotificationChannel(channel4.getId())); 714 } 715 testGetChannels()716 public void testGetChannels() throws Exception { 717 NotificationChannel channel1 = 718 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 719 NotificationChannel channel2 = 720 new NotificationChannel( 721 UUID.randomUUID().toString(), "name2", IMPORTANCE_HIGH); 722 NotificationChannel channel3 = 723 new NotificationChannel( 724 UUID.randomUUID().toString(), "name3", IMPORTANCE_LOW); 725 NotificationChannel channel4 = 726 new NotificationChannel( 727 UUID.randomUUID().toString(), "name4", IMPORTANCE_MIN); 728 729 Map<String, NotificationChannel> channelMap = new HashMap<>(); 730 channelMap.put(channel1.getId(), channel1); 731 channelMap.put(channel2.getId(), channel2); 732 channelMap.put(channel3.getId(), channel3); 733 channelMap.put(channel4.getId(), channel4); 734 mNotificationManager.createNotificationChannel(channel1); 735 mNotificationManager.createNotificationChannel(channel2); 736 mNotificationManager.createNotificationChannel(channel3); 737 mNotificationManager.createNotificationChannel(channel4); 738 739 mNotificationManager.deleteNotificationChannel(channel3.getId()); 740 741 List<NotificationChannel> channels = mNotificationManager.getNotificationChannels(); 742 for (NotificationChannel nc : channels) { 743 if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(nc.getId())) { 744 continue; 745 } 746 if (NOTIFICATION_CHANNEL_ID.equals(nc.getId())) { 747 continue; 748 } 749 assertFalse(channel3.getId().equals(nc.getId())); 750 if (!channelMap.containsKey(nc.getId())) { 751 // failed cleanup from prior test run; ignore 752 continue; 753 } 754 compareChannels(channelMap.get(nc.getId()), nc); 755 } 756 } 757 testRecreateDeletedChannel()758 public void testRecreateDeletedChannel() throws Exception { 759 NotificationChannel channel = 760 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 761 channel.setShowBadge(true); 762 NotificationChannel newChannel = new NotificationChannel( 763 channel.getId(), channel.getName(), IMPORTANCE_HIGH); 764 mNotificationManager.createNotificationChannel(channel); 765 mNotificationManager.deleteNotificationChannel(channel.getId()); 766 767 mNotificationManager.createNotificationChannel(newChannel); 768 769 compareChannels(channel, 770 mNotificationManager.getNotificationChannel(newChannel.getId())); 771 } 772 testNotify()773 public void testNotify() throws Exception { 774 mNotificationManager.cancelAll(); 775 776 final int id = 1; 777 sendNotification(id, R.drawable.black); 778 // test updating the same notification 779 sendNotification(id, R.drawable.blue); 780 sendNotification(id, R.drawable.yellow); 781 782 // assume that sendNotification tested to make sure individual notifications were present 783 StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications(); 784 for (StatusBarNotification sbn : sbns) { 785 if (sbn.getId() != id) { 786 fail("we got back other notifications besides the one we posted: " 787 + sbn.getKey()); 788 } 789 } 790 } 791 testNotify_nonexistentChannel_ignored()792 public void testNotify_nonexistentChannel_ignored() { 793 mNotificationManager.cancelAll(); 794 final int id = 404; 795 final Notification notification = 796 new Notification.Builder(mContext, "non_existent_channel") 797 .setSmallIcon(R.drawable.black) 798 .setContentText("This should not be posted!") 799 .build(); 800 801 mNotificationManager.notify(id, notification); 802 803 assertThat(mNotificationHelper.findPostedNotification(null, id, SEARCH_TYPE.APP)).isNull(); 804 assertThat(mNotificationManager.getActiveNotifications()).isEmpty(); 805 } 806 testSuspendPackage_withoutShellPermission()807 public void testSuspendPackage_withoutShellPermission() throws Exception { 808 if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) { 809 return; 810 } 811 812 try { 813 Process proc = Runtime.getRuntime().exec("cmd notification suspend_package " 814 + mContext.getPackageName()); 815 816 // read output of command 817 BufferedReader reader = 818 new BufferedReader(new InputStreamReader(proc.getInputStream())); 819 StringBuilder output = new StringBuilder(); 820 String line = reader.readLine(); 821 while (line != null) { 822 output.append(line); 823 line = reader.readLine(); 824 } 825 reader.close(); 826 final String outputString = output.toString(); 827 828 proc.waitFor(); 829 830 // check that the output string had an error / disallowed call since it didn't have 831 // shell permission to suspend the package 832 assertTrue(outputString, outputString.contains("error")); 833 assertTrue(outputString, outputString.contains("permission denied")); 834 } catch (InterruptedException e) { 835 fail("Unsuccessful shell command"); 836 } 837 } 838 testSuspendPackage()839 public void testSuspendPackage() throws Exception { 840 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 841 assertNotNull(mListener); 842 843 CountDownLatch notificationPostedLatch = mListener.setPostedCountDown(1); 844 sendNotification(1, R.drawable.black); 845 // wait for notification listener to receive notification 846 notificationPostedLatch.await(500, TimeUnit.MILLISECONDS); 847 assertEquals(1, mListener.mPosted.size()); 848 849 CountDownLatch notificationRankingLatch = mListener.setRankingUpdateCountDown(1); 850 // suspend package, ranking should be updated with suspended = true 851 suspendPackage(mContext.getPackageName(), InstrumentationRegistry.getInstrumentation(), 852 true); 853 // wait for notification listener to get response 854 notificationRankingLatch.await(500, TimeUnit.MILLISECONDS); 855 NotificationListenerService.RankingMap rankingMap = mListener.mRankingMap; 856 NotificationListenerService.Ranking outRanking = new NotificationListenerService.Ranking(); 857 for (String key : rankingMap.getOrderedKeys()) { 858 if (key.contains(mListener.getPackageName())) { 859 rankingMap.getRanking(key, outRanking); 860 Log.d(TAG, "key=" + key + " suspended=" + outRanking.isSuspended()); 861 assertTrue(outRanking.isSuspended()); 862 } 863 } 864 865 notificationRankingLatch = mListener.setRankingUpdateCountDown(1); 866 // unsuspend package, ranking should be updated with suspended = false 867 suspendPackage(mContext.getPackageName(), InstrumentationRegistry.getInstrumentation(), 868 false); 869 // wait for notification listener to get response 870 notificationRankingLatch.await(500, TimeUnit.MILLISECONDS); 871 rankingMap = mListener.mRankingMap; 872 for (String key : rankingMap.getOrderedKeys()) { 873 if (key.contains(mListener.getPackageName())) { 874 rankingMap.getRanking(key, outRanking); 875 Log.d(TAG, "key=" + key + " suspended=" + outRanking.isSuspended()); 876 assertFalse(outRanking.isSuspended()); 877 } 878 } 879 880 mListener.resetData(); 881 } 882 testSuspendedPackageSendsNotification()883 public void testSuspendedPackageSendsNotification() throws Exception { 884 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 885 assertNotNull(mListener); 886 CountDownLatch postedLatch = mListener.setPostedCountDown(1); 887 888 // suspend package, post notification while package is suspended, see notification 889 // in ranking map with suspended = true 890 suspendPackage(mContext.getPackageName(), InstrumentationRegistry.getInstrumentation(), 891 true); 892 sendNotification(1, R.drawable.black); 893 // wait for notification listener to receive notification 894 postedLatch.await(500, TimeUnit.MILLISECONDS); 895 assertEquals(1, mListener.mPosted.size()); // apps targeting P receive notification 896 NotificationListenerService.RankingMap rankingMap = mListener.mRankingMap; 897 NotificationListenerService.Ranking outRanking = new NotificationListenerService.Ranking(); 898 for (String key : rankingMap.getOrderedKeys()) { 899 if (key.contains(mListener.getPackageName())) { 900 rankingMap.getRanking(key, outRanking); 901 Log.d(TAG, "key=" + key + " suspended=" + outRanking.isSuspended()); 902 assertTrue(outRanking.isSuspended()); 903 } 904 } 905 906 // unsuspend package, ranking should be updated with suspended = false 907 suspendPackage(mContext.getPackageName(), InstrumentationRegistry.getInstrumentation(), 908 false); 909 Thread.sleep(500); // wait for notification listener to get response 910 assertEquals(1, mListener.mPosted.size()); // should see previously posted notification 911 rankingMap = mListener.mRankingMap; 912 for (String key : rankingMap.getOrderedKeys()) { 913 if (key.contains(mListener.getPackageName())) { 914 rankingMap.getRanking(key, outRanking); 915 Log.d(TAG, "key=" + key + " suspended=" + outRanking.isSuspended()); 916 assertFalse(outRanking.isSuspended()); 917 } 918 } 919 920 mListener.resetData(); 921 } 922 testShowBadging_ranking()923 public void testShowBadging_ranking() throws Exception { 924 final int originalBadging = Settings.Secure.getInt( 925 mContext.getContentResolver(), Settings.Secure.NOTIFICATION_BADGING); 926 927 SystemUtil.runWithShellPermissionIdentity(() -> 928 Settings.Secure.putInt(mContext.getContentResolver(), 929 Settings.Secure.NOTIFICATION_BADGING, 1)); 930 assertEquals(1, Settings.Secure.getInt( 931 mContext.getContentResolver(), Settings.Secure.NOTIFICATION_BADGING)); 932 933 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 934 assertNotNull(mListener); 935 CountDownLatch postedLatch = mListener.setPostedCountDown(1); 936 try { 937 sendNotification(1, R.drawable.black); 938 // wait for notification listener to receive notification 939 postedLatch.await(500, TimeUnit.MILLISECONDS); 940 NotificationListenerService.RankingMap rankingMap = mListener.mRankingMap; 941 NotificationListenerService.Ranking outRanking = 942 new NotificationListenerService.Ranking(); 943 for (String key : rankingMap.getOrderedKeys()) { 944 if (key.contains(mListener.getPackageName())) { 945 rankingMap.getRanking(key, outRanking); 946 assertTrue(outRanking.canShowBadge()); 947 } 948 } 949 950 CountDownLatch rankingUpdateLatch = mListener.setRankingUpdateCountDown(1); 951 952 // turn off badging globally 953 SystemUtil.runWithShellPermissionIdentity(() -> 954 Settings.Secure.putInt(mContext.getContentResolver(), 955 Settings.Secure.NOTIFICATION_BADGING, 0)); 956 957 // wait for ranking update 958 rankingUpdateLatch.await(500, TimeUnit.MILLISECONDS); 959 960 rankingMap = mListener.mRankingMap; 961 outRanking = new NotificationListenerService.Ranking(); 962 for (String key : rankingMap.getOrderedKeys()) { 963 if (key.contains(mListener.getPackageName())) { 964 assertFalse(outRanking.canShowBadge()); 965 } 966 } 967 968 mListener.resetData(); 969 } finally { 970 SystemUtil.runWithShellPermissionIdentity(() -> 971 Settings.Secure.putInt(mContext.getContentResolver(), 972 Settings.Secure.NOTIFICATION_BADGING, originalBadging)); 973 } 974 } 975 testKeyChannelGroupOverrideImportanceExplanation_ranking()976 public void testKeyChannelGroupOverrideImportanceExplanation_ranking() throws Exception { 977 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 978 assertNotNull(mListener); 979 980 CountDownLatch postedLatch = mListener.setPostedCountDown(1); 981 982 final int notificationId = 1; 983 sendNotification(notificationId, R.drawable.black); 984 // wait for notification listener to receive notification 985 postedLatch.await(500, TimeUnit.MILLISECONDS); 986 987 NotificationListenerService.RankingMap rankingMap = mListener.mRankingMap; 988 NotificationListenerService.Ranking outRanking = 989 new NotificationListenerService.Ranking(); 990 991 StatusBarNotification sbn = mNotificationHelper.findPostedNotification(null, notificationId, 992 SEARCH_TYPE.POSTED); 993 994 // check that the key and channel ids are the same in the ranking as the posted notification 995 for (String key : rankingMap.getOrderedKeys()) { 996 if (key.contains(mListener.getPackageName())) { 997 rankingMap.getRanking(key, outRanking); 998 999 // check notification key match 1000 assertEquals(sbn.getKey(), outRanking.getKey()); 1001 1002 // check notification channel ids match 1003 assertEquals(sbn.getNotification().getChannelId(), outRanking.getChannel().getId()); 1004 1005 // check override group key match 1006 assertEquals(sbn.getOverrideGroupKey(), outRanking.getOverrideGroupKey()); 1007 1008 // check importance explanation isn't null 1009 assertNotNull(outRanking.getImportanceExplanation()); 1010 } 1011 } 1012 } 1013 testNotify_blockedChannel()1014 public void testNotify_blockedChannel() throws Exception { 1015 mNotificationManager.cancelAll(); 1016 1017 NotificationChannel channel = 1018 new NotificationChannel(mId, "name", IMPORTANCE_NONE); 1019 mNotificationManager.createNotificationChannel(channel); 1020 1021 int id = 1; 1022 final Notification notification = 1023 new Notification.Builder(mContext, mId) 1024 .setSmallIcon(R.drawable.black) 1025 .setWhen(System.currentTimeMillis()) 1026 .setContentTitle("notify#" + id) 1027 .setContentText("This is #" + id + "notification ") 1028 .build(); 1029 mNotificationManager.notify(id, notification); 1030 1031 assertTrue(mNotificationHelper.isNotificationGone(id, SEARCH_TYPE.APP)); 1032 } 1033 testCancel()1034 public void testCancel() throws Exception { 1035 final int id = 9; 1036 sendNotification(id, R.drawable.black); 1037 // Wait for the notification posted not just enqueued 1038 try { 1039 Thread.sleep(500); 1040 } catch (InterruptedException e) { 1041 } 1042 mNotificationManager.cancel(id); 1043 1044 assertTrue(mNotificationHelper.isNotificationGone(id, SEARCH_TYPE.APP)); 1045 } 1046 testCancelAll()1047 public void testCancelAll() throws Exception { 1048 sendNotification(1, R.drawable.black); 1049 sendNotification(2, R.drawable.blue); 1050 sendNotification(3, R.drawable.yellow); 1051 1052 if (DEBUG) { 1053 Log.d(TAG, "posted 3 notifications, here they are: "); 1054 StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications(); 1055 for (StatusBarNotification sbn : sbns) { 1056 Log.d(TAG, " " + sbn); 1057 } 1058 Log.d(TAG, "about to cancel..."); 1059 } 1060 mNotificationManager.cancelAll(); 1061 1062 for (int id = 1; id <= 3; id++) { 1063 assertTrue(mNotificationHelper.isNotificationGone(id, SEARCH_TYPE.APP)); 1064 } 1065 1066 } 1067 testNotifyWithTimeout()1068 public void testNotifyWithTimeout() throws Exception { 1069 mNotificationManager.cancelAll(); 1070 final int id = 128; 1071 final long timeout = 1000; 1072 1073 final Notification notification = 1074 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 1075 .setSmallIcon(R.drawable.black) 1076 .setContentTitle("notify#" + id) 1077 .setContentText("This is #" + id + "notification ") 1078 .setTimeoutAfter(timeout) 1079 .build(); 1080 mNotificationManager.notify(id, notification); 1081 1082 assertNotNull(mNotificationHelper.findPostedNotification(null, id, SEARCH_TYPE.APP)); 1083 1084 try { 1085 Thread.sleep(timeout); 1086 } catch (InterruptedException ex) { 1087 // pass 1088 } 1089 assertTrue(mNotificationHelper.isNotificationGone(id, SEARCH_TYPE.APP)); 1090 } 1091 testStyle()1092 public void testStyle() throws Exception { 1093 Notification.Style style = new Notification.Style() { 1094 public boolean areNotificationsVisiblyDifferent(Notification.Style other) { 1095 return false; 1096 } 1097 }; 1098 1099 Notification.Builder builder = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID); 1100 style.setBuilder(builder); 1101 1102 Notification notification = null; 1103 try { 1104 notification = style.build(); 1105 } catch (IllegalArgumentException e) { 1106 fail(e.getMessage()); 1107 } 1108 1109 assertNotNull(notification); 1110 1111 Notification builderNotification = builder.build(); 1112 assertEquals(builderNotification, notification); 1113 } 1114 testStyle_getStandardView()1115 public void testStyle_getStandardView() throws Exception { 1116 Notification.Builder builder = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID); 1117 int layoutId = 0; 1118 1119 TestStyle overrideStyle = new TestStyle(); 1120 overrideStyle.setBuilder(builder); 1121 RemoteViews result = overrideStyle.testGetStandardView(layoutId); 1122 1123 assertNotNull(result); 1124 assertEquals(layoutId, result.getLayoutId()); 1125 } 1126 1127 private class TestStyle extends Notification.Style { areNotificationsVisiblyDifferent(Notification.Style other)1128 public boolean areNotificationsVisiblyDifferent(Notification.Style other) { 1129 return false; 1130 } 1131 testGetStandardView(int layoutId)1132 public RemoteViews testGetStandardView(int layoutId) { 1133 // Wrapper method, since getStandardView is protected and otherwise unused in Android 1134 return getStandardView(layoutId); 1135 } 1136 } 1137 testMediaStyle_empty()1138 public void testMediaStyle_empty() { 1139 Notification.MediaStyle style = new Notification.MediaStyle(); 1140 assertNotNull(style); 1141 } 1142 testMediaStyle()1143 public void testMediaStyle() { 1144 mNotificationManager.cancelAll(); 1145 final int id = 99; 1146 MediaSession session = new MediaSession(getContext(), "media"); 1147 1148 final Notification notification = 1149 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 1150 .setSmallIcon(R.drawable.black) 1151 .setContentTitle("notify#" + id) 1152 .setContentText("This is #" + id + "notification ") 1153 .addAction(new Notification.Action.Builder( 1154 Icon.createWithResource(getContext(), R.drawable.icon_black), 1155 "play", getPendingIntent()).build()) 1156 .addAction(new Notification.Action.Builder( 1157 Icon.createWithResource(getContext(), R.drawable.icon_blue), 1158 "pause", getPendingIntent()).build()) 1159 .setStyle(new Notification.MediaStyle() 1160 .setShowActionsInCompactView(0, 1) 1161 .setMediaSession(session.getSessionToken())) 1162 .build(); 1163 mNotificationManager.notify(id, notification); 1164 1165 assertNotNull(mNotificationHelper.findPostedNotification(null, id, SEARCH_TYPE.APP)); 1166 } 1167 testInboxStyle()1168 public void testInboxStyle() { 1169 final int id = 100; 1170 1171 final Notification notification = 1172 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 1173 .setSmallIcon(R.drawable.black) 1174 .setContentTitle("notify#" + id) 1175 .setContentText("This is #" + id + "notification ") 1176 .addAction(new Notification.Action.Builder( 1177 Icon.createWithResource(getContext(), R.drawable.icon_black), 1178 "a1", getPendingIntent()).build()) 1179 .addAction(new Notification.Action.Builder( 1180 Icon.createWithResource(getContext(), R.drawable.icon_blue), 1181 "a2", getPendingIntent()).build()) 1182 .setStyle(new Notification.InboxStyle().addLine("line") 1183 .setSummaryText("summary")) 1184 .build(); 1185 mNotificationManager.notify(id, notification); 1186 1187 assertNotNull(mNotificationHelper.findPostedNotification(null, id, SEARCH_TYPE.APP)); 1188 } 1189 testBigTextStyle()1190 public void testBigTextStyle() { 1191 final int id = 101; 1192 1193 final Notification notification = 1194 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 1195 .setSmallIcon(R.drawable.black) 1196 .setContentTitle("notify#" + id) 1197 .setContentText("This is #" + id + "notification ") 1198 .addAction(new Notification.Action.Builder( 1199 Icon.createWithResource(getContext(), R.drawable.icon_black), 1200 "a1", getPendingIntent()).build()) 1201 .addAction(new Notification.Action.Builder( 1202 Icon.createWithResource(getContext(), R.drawable.icon_blue), 1203 "a2", getPendingIntent()).build()) 1204 .setStyle(new Notification.BigTextStyle() 1205 .setBigContentTitle("big title") 1206 .bigText("big text") 1207 .setSummaryText("summary")) 1208 .build(); 1209 mNotificationManager.notify(id, notification); 1210 1211 assertNotNull(mNotificationHelper.findPostedNotification(null, id, SEARCH_TYPE.APP)); 1212 } 1213 testBigPictureStyle()1214 public void testBigPictureStyle() { 1215 final int id = 102; 1216 1217 final Notification notification = 1218 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 1219 .setSmallIcon(R.drawable.black) 1220 .setContentTitle("notify#" + id) 1221 .setContentText("This is #" + id + "notification ") 1222 .addAction(new Notification.Action.Builder( 1223 Icon.createWithResource(getContext(), R.drawable.icon_black), 1224 "a1", getPendingIntent()).build()) 1225 .addAction(new Notification.Action.Builder( 1226 Icon.createWithResource(getContext(), R.drawable.icon_blue), 1227 "a2", getPendingIntent()).build()) 1228 .setStyle(new Notification.BigPictureStyle() 1229 .setBigContentTitle("title") 1230 .bigPicture(Bitmap.createBitmap(100, 100, Bitmap.Config.RGB_565)) 1231 .bigLargeIcon( 1232 Icon.createWithResource(getContext(), R.drawable.icon_blue)) 1233 .setSummaryText("summary") 1234 .setContentDescription("content description")) 1235 .build(); 1236 mNotificationManager.notify(id, notification); 1237 1238 assertNotNull(mNotificationHelper.findPostedNotification(null, id, SEARCH_TYPE.APP)); 1239 } 1240 testAutogrouping()1241 public void testAutogrouping() throws Exception { 1242 sendNotification(801, R.drawable.black); 1243 sendNotification(802, R.drawable.blue); 1244 sendNotification(803, R.drawable.yellow); 1245 sendNotification(804, R.drawable.yellow); 1246 1247 assertNotificationCount(5); 1248 assertAllPostedNotificationsAutogrouped(); 1249 } 1250 testAutogrouping_autogroupStaysUntilAllNotificationsCanceled()1251 public void testAutogrouping_autogroupStaysUntilAllNotificationsCanceled() throws Exception { 1252 sendNotification(701, R.drawable.black); 1253 sendNotification(702, R.drawable.blue); 1254 sendNotification(703, R.drawable.yellow); 1255 sendNotification(704, R.drawable.yellow); 1256 1257 assertNotificationCount(5); 1258 assertAllPostedNotificationsAutogrouped(); 1259 1260 // Assert all notis stay in the same autogroup until all children are canceled 1261 for (int i = 704; i > 701; i--) { 1262 cancelAndPoll(i); 1263 assertNotificationCount(i - 700); 1264 assertAllPostedNotificationsAutogrouped(); 1265 } 1266 cancelAndPoll(701); 1267 assertNotificationCount(0); 1268 } 1269 testAutogrouping_autogroupStaysUntilAllNotificationsAddedToGroup()1270 public void testAutogrouping_autogroupStaysUntilAllNotificationsAddedToGroup() 1271 throws Exception { 1272 String newGroup = "new!"; 1273 sendNotification(901, R.drawable.black); 1274 sendNotification(902, R.drawable.blue); 1275 sendNotification(903, R.drawable.yellow); 1276 sendNotification(904, R.drawable.yellow); 1277 1278 List<Integer> postedIds = new ArrayList<>(); 1279 postedIds.add(901); 1280 postedIds.add(902); 1281 postedIds.add(903); 1282 postedIds.add(904); 1283 1284 assertNotificationCount(5); 1285 assertAllPostedNotificationsAutogrouped(); 1286 1287 // Assert all notis stay in the same autogroup until all children are canceled 1288 for (int i = 904; i > 901; i--) { 1289 sendNotification(i, newGroup, R.drawable.blue); 1290 postedIds.remove(postedIds.size() - 1); 1291 assertNotificationCount(5); 1292 assertOnlySomeNotificationsAutogrouped(postedIds); 1293 } 1294 sendNotification(901, newGroup, R.drawable.blue); 1295 assertNotificationCount(4); // no more autogroup summary 1296 postedIds.remove(0); 1297 assertOnlySomeNotificationsAutogrouped(postedIds); 1298 } 1299 testNewNotificationsAddedToAutogroup_ifOriginalNotificationsCanceled()1300 public void testNewNotificationsAddedToAutogroup_ifOriginalNotificationsCanceled() 1301 throws Exception { 1302 String newGroup = "new!"; 1303 sendNotification(910, R.drawable.black); 1304 sendNotification(920, R.drawable.blue); 1305 sendNotification(930, R.drawable.yellow); 1306 sendNotification(940, R.drawable.yellow); 1307 1308 List<Integer> postedIds = new ArrayList<>(); 1309 postedIds.add(910); 1310 postedIds.add(920); 1311 postedIds.add(930); 1312 postedIds.add(940); 1313 1314 assertNotificationCount(5); 1315 assertAllPostedNotificationsAutogrouped(); 1316 1317 // regroup all but one of the children 1318 for (int i = postedIds.size() - 1; i > 0; i--) { 1319 try { 1320 Thread.sleep(200); 1321 } catch (InterruptedException ex) { 1322 // pass 1323 } 1324 int id = postedIds.remove(i); 1325 sendNotification(id, newGroup, R.drawable.blue); 1326 assertNotificationCount(5); 1327 assertOnlySomeNotificationsAutogrouped(postedIds); 1328 } 1329 1330 // send a new non-grouped notification. since the autogroup summary still exists, 1331 // the notification should be added to it 1332 sendNotification(950, R.drawable.blue); 1333 postedIds.add(950); 1334 try { 1335 Thread.sleep(200); 1336 } catch (InterruptedException ex) { 1337 // pass 1338 } 1339 assertOnlySomeNotificationsAutogrouped(postedIds); 1340 } 1341 testPostFullScreenIntent_permission()1342 public void testPostFullScreenIntent_permission() { 1343 int id = 6000; 1344 1345 final Notification notification = 1346 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 1347 .setSmallIcon(R.drawable.black) 1348 .setWhen(System.currentTimeMillis()) 1349 .setFullScreenIntent(getPendingIntent(), true) 1350 .setContentText("This is #FSI notification") 1351 .setContentIntent(getPendingIntent()) 1352 .build(); 1353 mNotificationManager.notify(id, notification); 1354 1355 StatusBarNotification n = mNotificationHelper.findPostedNotification( 1356 null, id, SEARCH_TYPE.APP); 1357 assertNotNull(n); 1358 assertEquals(notification.fullScreenIntent, n.getNotification().fullScreenIntent); 1359 } 1360 testNotificationDelegate_grantAndPost()1361 public void testNotificationDelegate_grantAndPost() throws Exception { 1362 final Intent intent = new Intent(mContext, GetResultActivity.class); 1363 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1364 GetResultActivity activity = (GetResultActivity) mInstrumentation.startActivitySync(intent); 1365 mInstrumentation.waitForIdleSync(); 1366 activity.clearResult(); 1367 1368 // grant this test permission to post 1369 final Intent activityIntent = new Intent(); 1370 activityIntent.setPackage(TEST_APP); 1371 activityIntent.setAction(Intent.ACTION_MAIN); 1372 activityIntent.addCategory(Intent.CATEGORY_LAUNCHER); 1373 1374 activity.startActivityForResult(activityIntent, REQUEST_CODE); 1375 assertEquals(RESULT_OK, activity.getResult().resultCode); 1376 1377 // send notification 1378 Notification n = new Notification.Builder(mContext, "channel") 1379 .setSmallIcon(android.R.drawable.ic_media_play) 1380 .build(); 1381 mNotificationManager.notifyAsPackage(TEST_APP, "tag", 0, n); 1382 1383 assertNotNull(mNotificationHelper.findPostedNotification("tag", 0, SEARCH_TYPE.APP)); 1384 final Intent revokeIntent = new Intent(Intent.ACTION_MAIN); 1385 revokeIntent.setClassName(TEST_APP, REVOKE_CLASS); 1386 activity.startActivityForResult(revokeIntent, REQUEST_CODE); 1387 assertEquals(RESULT_OK, activity.getResult().resultCode); 1388 } 1389 testNotificationDelegate_grantAndPostAndCancel()1390 public void testNotificationDelegate_grantAndPostAndCancel() throws Exception { 1391 final Intent intent = new Intent(mContext, GetResultActivity.class); 1392 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1393 GetResultActivity activity = (GetResultActivity) mInstrumentation.startActivitySync(intent); 1394 mInstrumentation.waitForIdleSync(); 1395 activity.clearResult(); 1396 1397 // grant this test permission to post 1398 final Intent activityIntent = new Intent(); 1399 activityIntent.setPackage(TEST_APP); 1400 activityIntent.setAction(Intent.ACTION_MAIN); 1401 activityIntent.addCategory(Intent.CATEGORY_LAUNCHER); 1402 1403 activity.startActivityForResult(activityIntent, REQUEST_CODE); 1404 assertEquals(RESULT_OK, activity.getResult().resultCode); 1405 1406 // send notification 1407 Notification n = new Notification.Builder(mContext, "channel") 1408 .setSmallIcon(android.R.drawable.ic_media_play) 1409 .build(); 1410 mNotificationManager.notifyAsPackage(TEST_APP, "toBeCanceled", 10000, n); 1411 assertNotNull(mNotificationHelper.findPostedNotification("toBeCanceled", 10000, 1412 SEARCH_TYPE.APP)); 1413 mNotificationManager.cancelAsPackage(TEST_APP, "toBeCanceled", 10000); 1414 assertTrue(mNotificationHelper.isNotificationGone(10000, SEARCH_TYPE.APP)); 1415 final Intent revokeIntent = new Intent(Intent.ACTION_MAIN); 1416 revokeIntent.setClassName(TEST_APP, REVOKE_CLASS); 1417 activity.startActivityForResult(revokeIntent, REQUEST_CODE); 1418 assertEquals(RESULT_OK, activity.getResult().resultCode); 1419 } 1420 testNotificationDelegate_cannotCancelNotificationsPostedByDelegator()1421 public void testNotificationDelegate_cannotCancelNotificationsPostedByDelegator() 1422 throws Exception { 1423 final Intent intent = new Intent(mContext, GetResultActivity.class); 1424 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1425 GetResultActivity activity = (GetResultActivity) mInstrumentation.startActivitySync(intent); 1426 mInstrumentation.waitForIdleSync(); 1427 activity.clearResult(); 1428 1429 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 1430 assertNotNull(mListener); 1431 1432 // grant this test permission to post 1433 final Intent activityIntent = new Intent(Intent.ACTION_MAIN); 1434 activityIntent.setClassName(TEST_APP, DELEGATE_POST_CLASS); 1435 1436 activity.startActivityForResult(activityIntent, REQUEST_CODE); 1437 assertEquals(RESULT_OK, activity.getResult().resultCode); 1438 1439 assertNotNull(mNotificationHelper.findPostedNotification(null, 9, SEARCH_TYPE.LISTENER)); 1440 1441 try { 1442 mNotificationManager.cancelAsPackage(TEST_APP, null, 9); 1443 fail("Delegate should not be able to cancel notification they did not post"); 1444 } catch (SecurityException e) { 1445 // yay 1446 } 1447 1448 // double check that the notification does still exist 1449 assertNotNull(mNotificationHelper.findPostedNotification(null, 9, SEARCH_TYPE.LISTENER)); 1450 1451 final Intent revokeIntent = new Intent(Intent.ACTION_MAIN); 1452 revokeIntent.setClassName(TEST_APP, REVOKE_CLASS); 1453 activity.startActivityForResult(revokeIntent, REQUEST_CODE); 1454 assertEquals(RESULT_OK, activity.getResult().resultCode); 1455 } 1456 testNotificationDelegate_grantAndReadChannels()1457 public void testNotificationDelegate_grantAndReadChannels() throws Exception { 1458 final Intent intent = new Intent(mContext, GetResultActivity.class); 1459 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1460 GetResultActivity activity = (GetResultActivity) mInstrumentation.startActivitySync(intent); 1461 mInstrumentation.waitForIdleSync(); 1462 activity.clearResult(); 1463 1464 // grant this test permission to post 1465 final Intent activityIntent = new Intent(); 1466 activityIntent.setPackage(TEST_APP); 1467 activityIntent.setAction(Intent.ACTION_MAIN); 1468 activityIntent.addCategory(Intent.CATEGORY_LAUNCHER); 1469 1470 activity.startActivityForResult(activityIntent, REQUEST_CODE); 1471 assertEquals(RESULT_OK, activity.getResult().resultCode); 1472 1473 List<NotificationChannel> channels = 1474 mContext.createPackageContextAsUser(TEST_APP, /* flags= */ 0, mContext.getUser()) 1475 .getSystemService(NotificationManager.class) 1476 .getNotificationChannels(); 1477 1478 assertNotNull(channels); 1479 1480 final Intent revokeIntent = new Intent(Intent.ACTION_MAIN); 1481 revokeIntent.setClassName(TEST_APP, REVOKE_CLASS); 1482 activity.startActivityForResult(revokeIntent, REQUEST_CODE); 1483 assertEquals(RESULT_OK, activity.getResult().resultCode); 1484 } 1485 testNotificationDelegate_grantAndReadChannel()1486 public void testNotificationDelegate_grantAndReadChannel() throws Exception { 1487 final Intent intent = new Intent(mContext, GetResultActivity.class); 1488 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1489 GetResultActivity activity = (GetResultActivity) mInstrumentation.startActivitySync(intent); 1490 mInstrumentation.waitForIdleSync(); 1491 activity.clearResult(); 1492 1493 // grant this test permission to post 1494 final Intent activityIntent = new Intent(); 1495 activityIntent.setPackage(TEST_APP); 1496 activityIntent.setAction(Intent.ACTION_MAIN); 1497 activityIntent.addCategory(Intent.CATEGORY_LAUNCHER); 1498 1499 activity.startActivityForResult(activityIntent, REQUEST_CODE); 1500 assertEquals(RESULT_OK, activity.getResult().resultCode); 1501 1502 NotificationChannel channel = 1503 mContext.createPackageContextAsUser(TEST_APP, /* flags= */ 0, mContext.getUser()) 1504 .getSystemService(NotificationManager.class) 1505 .getNotificationChannel("channel"); 1506 1507 assertNotNull(channel); 1508 1509 final Intent revokeIntent = new Intent(Intent.ACTION_MAIN); 1510 revokeIntent.setClassName(TEST_APP, REVOKE_CLASS); 1511 activity.startActivityForResult(revokeIntent, REQUEST_CODE); 1512 assertEquals(RESULT_OK, activity.getResult().resultCode); 1513 } 1514 testNotificationDelegate_grantAndRevoke()1515 public void testNotificationDelegate_grantAndRevoke() throws Exception { 1516 final Intent intent = new Intent(mContext, GetResultActivity.class); 1517 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1518 GetResultActivity activity = (GetResultActivity) mInstrumentation.startActivitySync(intent); 1519 mInstrumentation.waitForIdleSync(); 1520 activity.clearResult(); 1521 1522 // grant this test permission to post 1523 final Intent activityIntent = new Intent(); 1524 activityIntent.setPackage(TEST_APP); 1525 activityIntent.setAction(Intent.ACTION_MAIN); 1526 activityIntent.addCategory(Intent.CATEGORY_LAUNCHER); 1527 1528 activity.startActivityForResult(activityIntent, REQUEST_CODE); 1529 assertEquals(RESULT_OK, activity.getResult().resultCode); 1530 1531 assertTrue(mNotificationManager.canNotifyAsPackage(TEST_APP)); 1532 1533 final Intent revokeIntent = new Intent(Intent.ACTION_MAIN); 1534 revokeIntent.setClassName(TEST_APP, REVOKE_CLASS); 1535 activity.startActivityForResult(revokeIntent, REQUEST_CODE); 1536 assertEquals(RESULT_OK, activity.getResult().resultCode); 1537 1538 try { 1539 // send notification 1540 Notification n = new Notification.Builder(mContext, "channel") 1541 .setSmallIcon(android.R.drawable.ic_media_play) 1542 .build(); 1543 mNotificationManager.notifyAsPackage(TEST_APP, "tag", 0, n); 1544 fail("Should not be able to post as a delegate when permission revoked"); 1545 } catch (SecurityException e) { 1546 // yay 1547 } 1548 } 1549 testNotificationIcon()1550 public void testNotificationIcon() { 1551 int id = 6000; 1552 1553 Notification notification = 1554 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 1555 .setSmallIcon(android.R.drawable.ic_media_play) 1556 .setWhen(System.currentTimeMillis()) 1557 .setFullScreenIntent(getPendingIntent(), true) 1558 .setContentText("This notification has a resource icon") 1559 .setContentIntent(getPendingIntent()) 1560 .build(); 1561 mNotificationManager.notify(id, notification); 1562 1563 notification = 1564 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 1565 .setSmallIcon(Icon.createWithResource(mContext, android.R.id.icon)) 1566 .setWhen(System.currentTimeMillis()) 1567 .setFullScreenIntent(getPendingIntent(), true) 1568 .setContentText("This notification has an Icon icon") 1569 .setContentIntent(getPendingIntent()) 1570 .build(); 1571 mNotificationManager.notify(id, notification); 1572 1573 StatusBarNotification n = mNotificationHelper.findPostedNotification( 1574 null, id, SEARCH_TYPE.APP); 1575 assertNotNull(n); 1576 } 1577 testShouldHideSilentStatusIcons()1578 public void testShouldHideSilentStatusIcons() throws Exception { 1579 try { 1580 mNotificationManager.shouldHideSilentStatusBarIcons(); 1581 fail("Non-privileged apps should not get this information"); 1582 } catch (SecurityException e) { 1583 // pass 1584 } 1585 1586 mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 1587 // no exception this time 1588 mNotificationManager.shouldHideSilentStatusBarIcons(); 1589 } 1590 1591 /* Confirm that the optional methods of TestNotificationListener still exist and 1592 * don't fail. */ testNotificationListenerMethods()1593 public void testNotificationListenerMethods() { 1594 NotificationListenerService listener = new TestNotificationListener(); 1595 listener.onListenerConnected(); 1596 1597 listener.onSilentStatusBarIconsVisibilityChanged(false); 1598 1599 listener.onNotificationPosted(null); 1600 listener.onNotificationPosted(null, null); 1601 1602 listener.onNotificationRemoved(null); 1603 listener.onNotificationRemoved(null, null); 1604 1605 listener.onNotificationChannelGroupModified("", UserHandle.CURRENT, null, 1606 NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_ADDED); 1607 listener.onNotificationChannelModified("", UserHandle.CURRENT, null, 1608 NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_ADDED); 1609 1610 listener.onListenerDisconnected(); 1611 } 1612 performNotificationProviderAction(@onNull String action)1613 private void performNotificationProviderAction(@NonNull String action) { 1614 // Create an intent to launch an activity which just posts or cancels notifications 1615 Intent activityIntent = new Intent(Intent.ACTION_MAIN); 1616 activityIntent.setClassName(NOTIFICATIONPROVIDER, RICH_NOTIFICATION_ACTIVITY); 1617 activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1618 activityIntent.putExtra("action", action); 1619 mContext.startActivity(activityIntent); 1620 } 1621 testNotificationUriPermissionsGranted()1622 public void testNotificationUriPermissionsGranted() throws Exception { 1623 Uri background7Uri = Uri.parse( 1624 "content://com.android.test.notificationprovider.provider/background7.png"); 1625 Uri background8Uri = Uri.parse( 1626 "content://com.android.test.notificationprovider.provider/background8.png"); 1627 1628 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 1629 assertNotNull(mListener); 1630 1631 CountDownLatch postedLatch = mListener.setPostedCountDown(1); 1632 1633 try { 1634 // Post #7 1635 performNotificationProviderAction("send-7"); 1636 1637 postedLatch.await(POST_TIMEOUT, TimeUnit.MILLISECONDS); 1638 assertEquals(background7Uri, getNotificationBackgroundImageUri(7)); 1639 assertTrue(mNotificationHelper.isNotificationGone(8, SEARCH_TYPE.LISTENER)); 1640 assertAccessible(background7Uri); 1641 assertInaccessible(background8Uri); 1642 1643 // Reset the notification posted latch. 1644 postedLatch = mListener.setPostedCountDown(1); 1645 1646 // Post #8 1647 performNotificationProviderAction("send-8"); 1648 1649 postedLatch.await(POST_TIMEOUT, TimeUnit.MILLISECONDS); 1650 assertEquals(background7Uri, getNotificationBackgroundImageUri(7)); 1651 assertEquals(background8Uri, getNotificationBackgroundImageUri(8)); 1652 assertAccessible(background7Uri); 1653 assertAccessible(background8Uri); 1654 1655 // Add a notification removed latch. 1656 CountDownLatch removedLatch = mListener.setRemovedCountDown(1); 1657 1658 // Cancel #7 1659 performNotificationProviderAction("cancel-7"); 1660 1661 removedLatch.await(POST_TIMEOUT, TimeUnit.MILLISECONDS); 1662 assertTrue(mNotificationHelper.isNotificationGone(7, SEARCH_TYPE.LISTENER)); 1663 assertEquals(background8Uri, getNotificationBackgroundImageUri(8)); 1664 assertInaccessible(background7Uri); 1665 assertAccessible(background8Uri); 1666 1667 // Reset the notification reemoved latch. 1668 removedLatch = mListener.setRemovedCountDown(1); 1669 1670 // Cancel #8 1671 performNotificationProviderAction("cancel-8"); 1672 1673 removedLatch.await(POST_TIMEOUT, TimeUnit.MILLISECONDS); 1674 assertTrue(mNotificationHelper.isNotificationGone(7, SEARCH_TYPE.LISTENER)); 1675 assertTrue(mNotificationHelper.isNotificationGone(8, SEARCH_TYPE.LISTENER)); 1676 assertInaccessible(background7Uri); 1677 assertInaccessible(background8Uri); 1678 1679 } finally { 1680 // Clean up -- reset any remaining notifications 1681 performNotificationProviderAction("reset"); 1682 Thread.sleep(500); 1683 } 1684 } 1685 testNotificationUriPermissionsGrantedToNewListeners()1686 public void testNotificationUriPermissionsGrantedToNewListeners() throws Exception { 1687 Uri background7Uri = Uri.parse( 1688 "content://com.android.test.notificationprovider.provider/background7.png"); 1689 1690 try { 1691 // Post #7 1692 performNotificationProviderAction("send-7"); 1693 // Don't have access the notification yet, but we can test the URI 1694 assertInaccessible(background7Uri); 1695 1696 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 1697 assertNotNull(mListener); 1698 1699 mNotificationHelper.findPostedNotification(null, 7, SEARCH_TYPE.LISTENER); 1700 1701 assertEquals(background7Uri, getNotificationBackgroundImageUri(7)); 1702 assertAccessible(background7Uri); 1703 1704 } finally { 1705 // Clean Up -- Cancel #7 1706 performNotificationProviderAction("cancel-7"); 1707 Thread.sleep(500); 1708 } 1709 } 1710 testNotificationUriPermissionsRevokedFromRemovedListeners()1711 public void testNotificationUriPermissionsRevokedFromRemovedListeners() throws Exception { 1712 Uri background7Uri = Uri.parse( 1713 "content://com.android.test.notificationprovider.provider/background7.png"); 1714 1715 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 1716 mListener = TestNotificationListener.getInstance(); 1717 assertNotNull(mListener); 1718 1719 try { 1720 // Post #7 1721 performNotificationProviderAction("send-7"); 1722 mNotificationHelper.findPostedNotification(null, 7, SEARCH_TYPE.POSTED); 1723 1724 assertEquals(background7Uri, getNotificationBackgroundImageUri(7)); 1725 assertAccessible(background7Uri); 1726 1727 // Remove the listener to ensure permissions get revoked 1728 mNotificationHelper.disableListener(STUB_PACKAGE_NAME); 1729 Thread.sleep(500); // wait for listener to be disabled 1730 1731 assertInaccessible(background7Uri); 1732 1733 } finally { 1734 // Clean Up -- Cancel #7 1735 performNotificationProviderAction("cancel-7"); 1736 Thread.sleep(500); 1737 } 1738 } 1739 1740 private class NotificationListenerConnection implements ServiceConnection { 1741 private final Semaphore mSemaphore = new Semaphore(0); 1742 1743 @Override onServiceConnected(ComponentName className, IBinder service)1744 public void onServiceConnected(ComponentName className, IBinder service) { 1745 if (URI_ACCESS_SERVICE.equals(className)) { 1746 mNotificationUriAccessService = INotificationUriAccessService.Stub.asInterface( 1747 service); 1748 } 1749 if (NLS_CONTROL_SERVICE.equals(className)) { 1750 mNLSControlService = INLSControlService.Stub.asInterface(service); 1751 } 1752 mSemaphore.release(); 1753 } 1754 1755 @Override onServiceDisconnected(ComponentName className)1756 public void onServiceDisconnected(ComponentName className) { 1757 if (URI_ACCESS_SERVICE.equals(className)) { 1758 mNotificationUriAccessService = null; 1759 } 1760 if (NLS_CONTROL_SERVICE.equals(className)) { 1761 mNLSControlService = null; 1762 } 1763 } 1764 waitForService()1765 public void waitForService() { 1766 try { 1767 if (mSemaphore.tryAcquire(5, TimeUnit.SECONDS)) { 1768 return; 1769 } 1770 } catch (InterruptedException e) { 1771 } 1772 fail("failed to connec to service"); 1773 } 1774 } 1775 testNotificationUriPermissionsRevokedOnlyFromRemovedListeners()1776 public void testNotificationUriPermissionsRevokedOnlyFromRemovedListeners() throws Exception { 1777 Uri background7Uri = Uri.parse( 1778 "content://com.android.test.notificationprovider.provider/background7.png"); 1779 1780 // Connect to a service in the NotificationListener app which allows us to validate URI 1781 // permissions granted to a second app, so that we show that permissions aren't being 1782 // revoked too broadly. 1783 final Intent intent = new Intent(); 1784 intent.setComponent(URI_ACCESS_SERVICE); 1785 NotificationListenerConnection connection = new NotificationListenerConnection(); 1786 mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE); 1787 connection.waitForService(); 1788 1789 // Before starting the test, make sure the service works, that there is no listener, and 1790 // that the URI starts inaccessible to that process. 1791 mNotificationUriAccessService.ensureNotificationListenerServiceConnected(false); 1792 assertFalse(mNotificationUriAccessService.isFileUriAccessible(background7Uri)); 1793 1794 // Give the NotificationListener app access to notifications, and validate that. 1795 toggleExternalListenerAccess(new ComponentName("com.android.test.notificationlistener", 1796 "com.android.test.notificationlistener.TestNotificationListener"), true); 1797 Thread.sleep(500); 1798 mNotificationUriAccessService.ensureNotificationListenerServiceConnected(true); 1799 assertFalse(mNotificationUriAccessService.isFileUriAccessible(background7Uri)); 1800 1801 // Give the test app access to notifications, and get that listener 1802 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 1803 assertNotNull(mListener); 1804 1805 try { 1806 try { 1807 // Post #7 1808 performNotificationProviderAction("send-7"); 1809 1810 // Check that both the test app (this code) and the external app have URI access. 1811 assertEquals(background7Uri, getNotificationBackgroundImageUri(7)); 1812 assertAccessible(background7Uri); 1813 assertTrue(mNotificationUriAccessService.isFileUriAccessible(background7Uri)); 1814 1815 // Remove the listener to ensure permissions get revoked 1816 mNotificationHelper.disableListener(STUB_PACKAGE_NAME); 1817 Thread.sleep(500); // wait for listener to be disabled 1818 1819 // Ensure that revoking listener access to this one app does not effect the other. 1820 assertInaccessible(background7Uri); 1821 assertTrue(mNotificationUriAccessService.isFileUriAccessible(background7Uri)); 1822 1823 } finally { 1824 // Clean Up -- Cancel #7 1825 performNotificationProviderAction("cancel-7"); 1826 Thread.sleep(500); 1827 } 1828 1829 // Finally, cancelling the permission must still revoke those other permissions. 1830 assertFalse(mNotificationUriAccessService.isFileUriAccessible(background7Uri)); 1831 1832 } finally { 1833 // Clean Up -- Make sure the external listener is has access revoked 1834 toggleExternalListenerAccess(new ComponentName("com.android.test.notificationlistener", 1835 "com.android.test.notificationlistener.TestNotificationListener"), false); 1836 } 1837 } 1838 testNotificationListenerRequestUnbind()1839 public void testNotificationListenerRequestUnbind() throws Exception { 1840 final Intent intent = new Intent(); 1841 intent.setComponent(NLS_CONTROL_SERVICE); 1842 NotificationListenerConnection connection = new NotificationListenerConnection(); 1843 mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE); 1844 connection.waitForService(); 1845 1846 // Give the NotificationListener app access to notifications, and validate that. 1847 toggleExternalListenerAccess(NO_AUTOBIND_NLS, true); 1848 Thread.sleep(500); 1849 1850 // Give the test app access to notifications, and get that listener 1851 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 1852 assertNotNull(mListener); 1853 1854 try { 1855 // Check that the listener service is not auto-bound (manifest meta-data) 1856 assertFalse(mNLSControlService.isNotificationListenerConnected()); 1857 1858 // Request bind NLS 1859 mNLSControlService.requestRebindComponent(); 1860 Thread.sleep(500); 1861 assertTrue(mNLSControlService.isNotificationListenerConnected()); 1862 1863 // Request unbind NLS 1864 mNLSControlService.requestUnbindComponent(); 1865 Thread.sleep(500); 1866 assertFalse(mNLSControlService.isNotificationListenerConnected()); 1867 } finally { 1868 // Clean Up -- Make sure the external listener is has access revoked 1869 toggleExternalListenerAccess(NO_AUTOBIND_NLS, false); 1870 } 1871 } 1872 testNotificationListenerAutobindMetaData()1873 public void testNotificationListenerAutobindMetaData() throws Exception { 1874 final ServiceInfo info = mPackageManager.getServiceInfo(NO_AUTOBIND_NLS, 1875 PackageManager.GET_META_DATA 1876 | PackageManager.MATCH_DIRECT_BOOT_AWARE 1877 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE); 1878 1879 assertNotNull(info); 1880 assertTrue(info.metaData.containsKey(META_DATA_DEFAULT_AUTOBIND)); 1881 assertFalse(info.metaData.getBoolean(META_DATA_DEFAULT_AUTOBIND, true)); 1882 } 1883 assertAccessible(Uri uri)1884 private void assertAccessible(Uri uri) 1885 throws IOException { 1886 ContentResolver contentResolver = mContext.getContentResolver(); 1887 for (int tries = 3; tries-- > 0; ) { 1888 try (AssetFileDescriptor fd = contentResolver.openAssetFile(uri, "r", null)) { 1889 if (fd != null) { 1890 return; 1891 } 1892 } catch (SecurityException e) { 1893 } 1894 try { 1895 Thread.sleep(100); 1896 } catch (InterruptedException ex) { 1897 } 1898 } 1899 fail("Uri " + uri + "is not accessible"); 1900 } 1901 assertInaccessible(Uri uri)1902 private void assertInaccessible(Uri uri) 1903 throws IOException { 1904 ContentResolver contentResolver = mContext.getContentResolver(); 1905 for (int tries = 3; tries-- > 0; ) { 1906 try (AssetFileDescriptor fd = contentResolver.openAssetFile(uri, "r", null)) { 1907 } catch (SecurityException e) { 1908 return; 1909 } 1910 try { 1911 Thread.sleep(100); 1912 } catch (InterruptedException ex) { 1913 } 1914 } 1915 fail("Uri " + uri + "is still accessible"); 1916 } 1917 1918 @NonNull getNotificationBackgroundImageUri(int notificationId)1919 private Uri getNotificationBackgroundImageUri(int notificationId) { 1920 StatusBarNotification sbn = mNotificationHelper.findPostedNotification(null, 1921 notificationId, SEARCH_TYPE.LISTENER); 1922 assertNotNull(sbn); 1923 String imageUriString = sbn.getNotification().extras 1924 .getString(Notification.EXTRA_BACKGROUND_IMAGE_URI); 1925 assertNotNull(imageUriString); 1926 return Uri.parse(imageUriString); 1927 } 1928 uncheck(ThrowingSupplier<T> supplier)1929 private <T> T uncheck(ThrowingSupplier<T> supplier) { 1930 try { 1931 return supplier.get(); 1932 } catch (Exception e) { 1933 throw new CompletionException(e); 1934 } 1935 } 1936 testNotificationListener_setNotificationsShown()1937 public void testNotificationListener_setNotificationsShown() throws Exception { 1938 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 1939 assertNotNull(mListener); 1940 CountDownLatch postedLatch = mListener.setPostedCountDown(2); 1941 final int notificationId1 = 1003; 1942 final int notificationId2 = 1004; 1943 1944 sendNotification(notificationId1, R.drawable.black); 1945 sendNotification(notificationId2, R.drawable.black); 1946 // wait for notification listener to receive notification 1947 postedLatch.await(500, TimeUnit.MILLISECONDS); 1948 1949 StatusBarNotification sbn1 = mNotificationHelper.findPostedNotification( 1950 null, notificationId1, SEARCH_TYPE.LISTENER); 1951 StatusBarNotification sbn2 = mNotificationHelper.findPostedNotification( 1952 null, notificationId2, SEARCH_TYPE.LISTENER); 1953 mListener.setNotificationsShown(new String[]{sbn1.getKey()}); 1954 1955 mNotificationHelper.disableListener(STUB_PACKAGE_NAME); 1956 Thread.sleep(500); // wait for listener to be disallowed 1957 try { 1958 mListener.setNotificationsShown(new String[]{sbn2.getKey()}); 1959 fail("Should not be able to set shown if listener access isn't granted"); 1960 } catch (SecurityException e) { 1961 // expected 1962 } 1963 } 1964 testNotificationListener_getNotificationChannels()1965 public void testNotificationListener_getNotificationChannels() throws Exception { 1966 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 1967 assertNotNull(mListener); 1968 1969 try { 1970 mListener.getNotificationChannels(mContext.getPackageName(), UserHandle.CURRENT); 1971 fail("Shouldn't be able get channels without CompanionDeviceManager#getAssociations()"); 1972 } catch (SecurityException e) { 1973 // expected 1974 } 1975 } 1976 testNotificationListener_getNotificationChannelGroups()1977 public void testNotificationListener_getNotificationChannelGroups() throws Exception { 1978 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 1979 assertNotNull(mListener); 1980 try { 1981 mListener.getNotificationChannelGroups(mContext.getPackageName(), UserHandle.CURRENT); 1982 fail("Should not be able get groups without CompanionDeviceManager#getAssociations()"); 1983 } catch (SecurityException e) { 1984 // expected 1985 } 1986 } 1987 testNotificationListener_updateNotificationChannel()1988 public void testNotificationListener_updateNotificationChannel() throws Exception { 1989 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 1990 assertNotNull(mListener); 1991 1992 NotificationChannel channel = new NotificationChannel( 1993 NOTIFICATION_CHANNEL_ID, "name", IMPORTANCE_DEFAULT); 1994 try { 1995 mListener.updateNotificationChannel(mContext.getPackageName(), UserHandle.CURRENT, 1996 channel); 1997 fail("Shouldn't be able to update channel without " 1998 + "CompanionDeviceManager#getAssociations()"); 1999 } catch (SecurityException e) { 2000 // expected 2001 } 2002 } 2003 testNotificationListener_getActiveNotifications()2004 public void testNotificationListener_getActiveNotifications() throws Exception { 2005 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 2006 assertNotNull(mListener); 2007 CountDownLatch postedLatch = mListener.setPostedCountDown(2); 2008 final int notificationId1 = 1001; 2009 final int notificationId2 = 1002; 2010 2011 sendNotification(notificationId1, R.drawable.black); 2012 sendNotification(notificationId2, R.drawable.black); 2013 // wait for notification listener to receive notification 2014 postedLatch.await(500, TimeUnit.MILLISECONDS); 2015 2016 StatusBarNotification sbn1 = mNotificationHelper.findPostedNotification( 2017 null, notificationId1, SEARCH_TYPE.LISTENER); 2018 StatusBarNotification sbn2 = mNotificationHelper.findPostedNotification( 2019 null, notificationId2, SEARCH_TYPE.LISTENER); 2020 StatusBarNotification[] notifs = 2021 mListener.getActiveNotifications(new String[]{sbn2.getKey(), sbn1.getKey()}); 2022 assertEquals(sbn2.getKey(), notifs[0].getKey()); 2023 assertEquals(sbn2.getId(), notifs[0].getId()); 2024 assertEquals(sbn2.getPackageName(), notifs[0].getPackageName()); 2025 2026 assertEquals(sbn1.getKey(), notifs[1].getKey()); 2027 assertEquals(sbn1.getId(), notifs[1].getId()); 2028 assertEquals(sbn1.getPackageName(), notifs[1].getPackageName()); 2029 } 2030 2031 testNotificationListener_getCurrentRanking()2032 public void testNotificationListener_getCurrentRanking() throws Exception { 2033 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 2034 assertNotNull(mListener); 2035 CountDownLatch rankingUpdateLatch = mListener.setRankingUpdateCountDown(1); 2036 2037 sendNotification(1, R.drawable.black); 2038 rankingUpdateLatch.await(POST_TIMEOUT, TimeUnit.MILLISECONDS); 2039 mNotificationHelper.findPostedNotification(null, 1, SEARCH_TYPE.POSTED); 2040 2041 assertEquals(mListener.mRankingMap, mListener.getCurrentRanking()); 2042 } 2043 testNotificationListener_cancelNotifications()2044 public void testNotificationListener_cancelNotifications() throws Exception { 2045 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 2046 assertNotNull(mListener); 2047 final int notificationId = 1006; 2048 2049 CountDownLatch postedLatch = mListener.setPostedCountDown(1); 2050 sendNotification(notificationId, R.drawable.black); 2051 // wait for notification listener to receive notification 2052 postedLatch.await(POST_TIMEOUT, TimeUnit.MILLISECONDS); 2053 2054 StatusBarNotification sbn = mNotificationHelper.findPostedNotification(null, notificationId, 2055 SEARCH_TYPE.LISTENER); 2056 2057 mListener.cancelNotification(sbn.getPackageName(), sbn.getTag(), sbn.getId()); 2058 // Beginning with Lollipop, this cancelNotification signature no longer cancels the 2059 // notification. 2060 if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) { 2061 assertNotNull(mNotificationHelper.findPostedNotification(null, notificationId, 2062 SEARCH_TYPE.LISTENER)); 2063 } else { 2064 // Tested in LegacyNotificationManager20Test 2065 assertTrue(mNotificationHelper.isNotificationGone( 2066 notificationId, SEARCH_TYPE.LISTENER)); 2067 } 2068 2069 mListener.cancelNotifications(new String[]{sbn.getKey()}); 2070 if (getCancellationReason(sbn.getKey()) 2071 != NotificationListenerService.REASON_LISTENER_CANCEL) { 2072 fail("Failed to cancel notification id=" + notificationId); 2073 } 2074 } 2075 testNotificationAssistant_cancelNotifications()2076 public void testNotificationAssistant_cancelNotifications() throws Exception { 2077 mAssistant = mNotificationHelper.enableAssistant(STUB_PACKAGE_NAME); 2078 assertNotNull(mAssistant); 2079 final int notificationId = 1006; 2080 2081 sendNotification(notificationId, R.drawable.black); 2082 Thread.sleep(500); // wait for notification listener to receive notification 2083 2084 StatusBarNotification sbn = mNotificationHelper.findPostedNotification(null, notificationId, 2085 SEARCH_TYPE.APP); 2086 2087 mAssistant.cancelNotifications(new String[]{sbn.getKey()}); 2088 int gotReason = getAssistantCancellationReason(sbn.getKey()); 2089 if (gotReason != NotificationListenerService.REASON_ASSISTANT_CANCEL) { 2090 fail("Failed cancellation from assistant, notification id=" + notificationId 2091 + "; got reason=" + gotReason); 2092 } 2093 } 2094 testNotificationManagerPolicy_priorityCategoriesToString()2095 public void testNotificationManagerPolicy_priorityCategoriesToString() { 2096 String zeroString = NotificationManager.Policy.priorityCategoriesToString(0); 2097 assertEquals("priorityCategories of 0 produces empty string", "", zeroString); 2098 2099 String oneString = NotificationManager.Policy.priorityCategoriesToString(1); 2100 assertNotNull("priorityCategories of 1 returns a string", oneString); 2101 boolean lengthGreaterThanZero = oneString.length() > 0; 2102 assertTrue("priorityCategories of 1 returns a string with length greater than 0", 2103 lengthGreaterThanZero); 2104 2105 String badNumberString = NotificationManager.Policy.priorityCategoriesToString(1234567); 2106 assertNotNull("priorityCategories with a non-relevant int returns a string", 2107 badNumberString); 2108 } 2109 testNotificationManagerPolicy_prioritySendersToString()2110 public void testNotificationManagerPolicy_prioritySendersToString() { 2111 String zeroString = NotificationManager.Policy.prioritySendersToString(0); 2112 assertNotNull("prioritySenders of 1 returns a string", zeroString); 2113 boolean lengthGreaterThanZero = zeroString.length() > 0; 2114 assertTrue("prioritySenders of 1 returns a string with length greater than 0", 2115 lengthGreaterThanZero); 2116 2117 String badNumberString = NotificationManager.Policy.prioritySendersToString(1234567); 2118 assertNotNull("prioritySenders with a non-relevant int returns a string", badNumberString); 2119 } 2120 testNotificationManagerPolicy_suppressedEffectsToString()2121 public void testNotificationManagerPolicy_suppressedEffectsToString() { 2122 String zeroString = NotificationManager.Policy.suppressedEffectsToString(0); 2123 assertEquals("suppressedEffects of 0 produces empty string", "", zeroString); 2124 2125 String oneString = NotificationManager.Policy.suppressedEffectsToString(1); 2126 assertNotNull("suppressedEffects of 1 returns a string", oneString); 2127 boolean lengthGreaterThanZero = oneString.length() > 0; 2128 assertTrue("suppressedEffects of 1 returns a string with length greater than 0", 2129 lengthGreaterThanZero); 2130 2131 String badNumberString = NotificationManager.Policy.suppressedEffectsToString(1234567); 2132 assertNotNull("suppressedEffects with a non-relevant int returns a string", 2133 badNumberString); 2134 } 2135 testOriginalChannelImportance()2136 public void testOriginalChannelImportance() { 2137 NotificationChannel channel = new NotificationChannel(mId, "my channel", IMPORTANCE_HIGH); 2138 2139 mNotificationManager.createNotificationChannel(channel); 2140 2141 NotificationChannel actual = mNotificationManager.getNotificationChannel(channel.getId()); 2142 assertEquals(IMPORTANCE_HIGH, actual.getImportance()); 2143 assertEquals(IMPORTANCE_HIGH, actual.getOriginalImportance()); 2144 2145 // Apps are allowed to downgrade channel importance if the user has not changed any 2146 // fields on this channel yet. 2147 channel.setImportance(IMPORTANCE_DEFAULT); 2148 mNotificationManager.createNotificationChannel(channel); 2149 2150 actual = mNotificationManager.getNotificationChannel(channel.getId()); 2151 assertEquals(IMPORTANCE_DEFAULT, actual.getImportance()); 2152 assertEquals(IMPORTANCE_HIGH, actual.getOriginalImportance()); 2153 } 2154 testCreateConversationChannel()2155 public void testCreateConversationChannel() { 2156 final NotificationChannel channel = 2157 new NotificationChannel(mId, "Messages", IMPORTANCE_DEFAULT); 2158 2159 String conversationId = "person a"; 2160 2161 final NotificationChannel conversationChannel = 2162 new NotificationChannel(mId + "child", 2163 "Messages from " + conversationId, IMPORTANCE_DEFAULT); 2164 conversationChannel.setConversationId(channel.getId(), conversationId); 2165 2166 mNotificationManager.createNotificationChannel(channel); 2167 mNotificationManager.createNotificationChannel(conversationChannel); 2168 2169 compareChannels(conversationChannel, 2170 mNotificationManager.getNotificationChannel(channel.getId(), conversationId)); 2171 } 2172 testConversationRankingFields()2173 public void testConversationRankingFields() throws Exception { 2174 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 2175 assertNotNull(mListener); 2176 CountDownLatch postedLatch = mListener.setPostedCountDown(1); 2177 2178 createDynamicShortcut(); 2179 mNotificationManager.notify(177, getConversationNotification().build()); 2180 2181 // wait for notification listener to receive notification 2182 postedLatch.await(500, TimeUnit.MILLISECONDS); 2183 assertNotNull(mNotificationHelper.findPostedNotification(null, 177, SEARCH_TYPE.LISTENER)); 2184 assertEquals(1, mListener.mPosted.size()); 2185 2186 NotificationListenerService.RankingMap rankingMap = mListener.mRankingMap; 2187 NotificationListenerService.Ranking outRanking = new NotificationListenerService.Ranking(); 2188 for (String key : rankingMap.getOrderedKeys()) { 2189 if (key.contains(mListener.getPackageName())) { 2190 rankingMap.getRanking(key, outRanking); 2191 assertTrue(outRanking.isConversation()); 2192 assertEquals(SHARE_SHORTCUT_ID, outRanking.getConversationShortcutInfo().getId()); 2193 } 2194 } 2195 } 2196 testDemoteConversationChannel()2197 public void testDemoteConversationChannel() { 2198 final NotificationChannel channel = 2199 new NotificationChannel(mId, "Messages", IMPORTANCE_DEFAULT); 2200 2201 String conversationId = "person a"; 2202 2203 final NotificationChannel conversationChannel = 2204 new NotificationChannel(mId + "child", 2205 "Messages from " + conversationId, IMPORTANCE_DEFAULT); 2206 conversationChannel.setConversationId(channel.getId(), conversationId); 2207 2208 mNotificationManager.createNotificationChannel(channel); 2209 mNotificationManager.createNotificationChannel(conversationChannel); 2210 2211 conversationChannel.setDemoted(true); 2212 2213 SystemUtil.runWithShellPermissionIdentity(() -> 2214 mNotificationManager.updateNotificationChannel( 2215 mContext.getPackageName(), android.os.Process.myUid(), channel)); 2216 2217 assertEquals(false, mNotificationManager.getNotificationChannel( 2218 channel.getId(), conversationId).isDemoted()); 2219 } 2220 testDeleteConversationChannels()2221 public void testDeleteConversationChannels() throws Exception { 2222 setUpNotifListener(); 2223 CountDownLatch postedLatch = mListener.setPostedCountDown(1); 2224 2225 createDynamicShortcut(); 2226 2227 final NotificationChannel channel = 2228 new NotificationChannel(mId, "Messages", IMPORTANCE_DEFAULT); 2229 2230 final NotificationChannel conversationChannel = 2231 new NotificationChannel(mId + "child", 2232 "Messages from " + SHARE_SHORTCUT_ID, IMPORTANCE_DEFAULT); 2233 conversationChannel.setConversationId(channel.getId(), SHARE_SHORTCUT_ID); 2234 2235 mNotificationManager.createNotificationChannel(channel); 2236 mNotificationManager.createNotificationChannel(conversationChannel); 2237 2238 mNotificationManager.notify(177, getConversationNotification().build()); 2239 2240 // wait for notification listener to receive notification 2241 postedLatch.await(500, TimeUnit.MILLISECONDS); 2242 assertNotNull(mNotificationHelper.findPostedNotification(null, 177, SEARCH_TYPE.LISTENER)); 2243 assertEquals(1, mListener.mPosted.size()); 2244 2245 deleteShortcuts(); 2246 2247 Thread.sleep(300); // wait for deletion to propagate 2248 2249 assertFalse(mNotificationManager.getNotificationChannel(channel.getId(), 2250 conversationChannel.getConversationId()).isConversation()); 2251 2252 } 2253 2254 /** 2255 * This method verifies that an app can't bypass background restrictions by retrieving their own 2256 * notification and triggering it. 2257 */ 2258 @AsbSecurityTest(cveBugId = 185388103) testActivityStartFromRetrievedNotification_isBlocked()2259 public void testActivityStartFromRetrievedNotification_isBlocked() throws Exception { 2260 deactivateGracePeriod(); 2261 EventCallback callback = new EventCallback(); 2262 int notificationId = 6007; 2263 2264 // Post notification and fire its pending intent 2265 sendTrampolineMessage(TRAMPOLINE_SERVICE_API_30, MESSAGE_SERVICE_NOTIFICATION, 2266 notificationId, callback); 2267 PollingCheck.waitFor(TIMEOUT_MS, () -> uncheck(() -> { 2268 sendTrampolineMessage(TRAMPOLINE_SERVICE, MESSAGE_CLICK_NOTIFICATION, notificationId, 2269 callback); 2270 // timeoutMs = 1ms below because surrounding waitFor already handles retry & timeout. 2271 return callback.waitFor(EventCallback.NOTIFICATION_CLICKED, /* timeoutMs */ 1); 2272 })); 2273 2274 assertFalse("Activity start should have been blocked", 2275 callback.waitFor(EventCallback.ACTIVITY_STARTED, TIMEOUT_MS)); 2276 } 2277 testActivityStartOnBroadcastTrampoline_isBlocked()2278 public void testActivityStartOnBroadcastTrampoline_isBlocked() throws Exception { 2279 deactivateGracePeriod(); 2280 setUpNotifListener(); 2281 mListener.addTestPackage(TRAMPOLINE_APP); 2282 CountDownLatch postedLatch = mListener.setPostedCountDown(1); 2283 EventCallback callback = new EventCallback(); 2284 int notificationId = 6001; 2285 2286 // Post notification and fire its pending intent 2287 sendTrampolineMessage(TRAMPOLINE_SERVICE, MESSAGE_BROADCAST_NOTIFICATION, notificationId, 2288 callback); 2289 postedLatch.await(POST_TIMEOUT, TimeUnit.MILLISECONDS); 2290 StatusBarNotification statusBarNotification = mNotificationHelper.findPostedNotification( 2291 null, notificationId, SEARCH_TYPE.LISTENER); 2292 assertNotNull("Notification not posted on time", statusBarNotification); 2293 statusBarNotification.getNotification().contentIntent.send(); 2294 2295 assertTrue("Broadcast not received on time", 2296 callback.waitFor(EventCallback.BROADCAST_RECEIVED, TIMEOUT_LONG_MS)); 2297 assertFalse("Activity start should have been blocked", 2298 callback.waitFor(EventCallback.ACTIVITY_STARTED, TIMEOUT_MS)); 2299 } 2300 testActivityStartOnServiceTrampoline_isBlocked()2301 public void testActivityStartOnServiceTrampoline_isBlocked() throws Exception { 2302 deactivateGracePeriod(); 2303 setUpNotifListener(); 2304 mListener.addTestPackage(TRAMPOLINE_APP); 2305 CountDownLatch postedLatch = mListener.setPostedCountDown(1); 2306 EventCallback callback = new EventCallback(); 2307 int notificationId = 6002; 2308 2309 // Post notification and fire its pending intent 2310 sendTrampolineMessage(TRAMPOLINE_SERVICE, MESSAGE_SERVICE_NOTIFICATION, notificationId, 2311 callback); 2312 postedLatch.await(POST_TIMEOUT, TimeUnit.MILLISECONDS); 2313 StatusBarNotification statusBarNotification = mNotificationHelper.findPostedNotification( 2314 null, notificationId, SEARCH_TYPE.LISTENER); 2315 assertNotNull("Notification not posted on time", statusBarNotification); 2316 statusBarNotification.getNotification().contentIntent.send(); 2317 2318 assertTrue("Service not started on time", 2319 callback.waitFor(EventCallback.SERVICE_STARTED, TIMEOUT_MS)); 2320 assertFalse("Activity start should have been blocked", 2321 callback.waitFor(EventCallback.ACTIVITY_STARTED, TIMEOUT_MS)); 2322 } 2323 testActivityStartOnBroadcastTrampoline_whenApi30_isAllowed()2324 public void testActivityStartOnBroadcastTrampoline_whenApi30_isAllowed() throws Exception { 2325 deactivateGracePeriod(); 2326 setUpNotifListener(); 2327 mListener.addTestPackage(TRAMPOLINE_APP_API_30); 2328 CountDownLatch postedLatch = mListener.setPostedCountDown(1); 2329 EventCallback callback = new EventCallback(); 2330 int notificationId = 6003; 2331 2332 // Post notification and fire its pending intent 2333 sendTrampolineMessage(TRAMPOLINE_SERVICE_API_30, MESSAGE_BROADCAST_NOTIFICATION, 2334 notificationId, callback); 2335 postedLatch.await(POST_TIMEOUT, TimeUnit.MILLISECONDS); 2336 StatusBarNotification statusBarNotification = mNotificationHelper.findPostedNotification( 2337 null, notificationId, SEARCH_TYPE.LISTENER); 2338 assertNotNull("Notification not posted on time", statusBarNotification); 2339 statusBarNotification.getNotification().contentIntent.send(); 2340 2341 assertTrue("Broadcast not received on time", 2342 callback.waitFor(EventCallback.BROADCAST_RECEIVED, TIMEOUT_LONG_MS)); 2343 assertTrue("Activity not started", 2344 callback.waitFor(EventCallback.ACTIVITY_STARTED, TIMEOUT_MS)); 2345 } 2346 testActivityStartOnServiceTrampoline_whenApi30_isAllowed()2347 public void testActivityStartOnServiceTrampoline_whenApi30_isAllowed() throws Exception { 2348 deactivateGracePeriod(); 2349 setUpNotifListener(); 2350 mListener.addTestPackage(TRAMPOLINE_APP_API_30); 2351 CountDownLatch postedLatch = mListener.setPostedCountDown(1); 2352 EventCallback callback = new EventCallback(); 2353 int notificationId = 6004; 2354 2355 // Post notification and fire its pending intent 2356 sendTrampolineMessage(TRAMPOLINE_SERVICE_API_30, MESSAGE_SERVICE_NOTIFICATION, 2357 notificationId, callback); 2358 postedLatch.await(POST_TIMEOUT, TimeUnit.MILLISECONDS); 2359 StatusBarNotification statusBarNotification = mNotificationHelper.findPostedNotification( 2360 null, notificationId, SEARCH_TYPE.LISTENER); 2361 assertNotNull("Notification not posted on time", statusBarNotification); 2362 statusBarNotification.getNotification().contentIntent.send(); 2363 2364 assertTrue("Service not started on time", 2365 callback.waitFor(EventCallback.SERVICE_STARTED, TIMEOUT_MS)); 2366 assertTrue("Activity not started", 2367 callback.waitFor(EventCallback.ACTIVITY_STARTED, TIMEOUT_MS)); 2368 } 2369 testActivityStartOnBroadcastTrampoline_whenDefaultBrowser_isBlocked()2370 public void testActivityStartOnBroadcastTrampoline_whenDefaultBrowser_isBlocked() 2371 throws Exception { 2372 deactivateGracePeriod(); 2373 setDefaultBrowser(TRAMPOLINE_APP); 2374 setUpNotifListener(); 2375 mListener.addTestPackage(TRAMPOLINE_APP); 2376 CountDownLatch postedLatch = mListener.setPostedCountDown(1); 2377 EventCallback callback = new EventCallback(); 2378 int notificationId = 6005; 2379 2380 // Post notification and fire its pending intent 2381 sendTrampolineMessage(TRAMPOLINE_SERVICE, MESSAGE_BROADCAST_NOTIFICATION, notificationId, 2382 callback); 2383 postedLatch.await(POST_TIMEOUT, TimeUnit.MILLISECONDS); 2384 StatusBarNotification statusBarNotification = mNotificationHelper.findPostedNotification( 2385 null, notificationId, SEARCH_TYPE.LISTENER); 2386 assertNotNull("Notification not posted on time", statusBarNotification); 2387 statusBarNotification.getNotification().contentIntent.send(); 2388 2389 assertTrue("Broadcast not received on time", 2390 callback.waitFor(EventCallback.BROADCAST_RECEIVED, TIMEOUT_LONG_MS)); 2391 assertFalse("Activity started", 2392 callback.waitFor(EventCallback.ACTIVITY_STARTED, TIMEOUT_MS)); 2393 } 2394 testActivityStartOnBroadcastTrampoline_whenDefaultBrowserApi32_isAllowed()2395 public void testActivityStartOnBroadcastTrampoline_whenDefaultBrowserApi32_isAllowed() 2396 throws Exception { 2397 deactivateGracePeriod(); 2398 setDefaultBrowser(TRAMPOLINE_APP_API_32); 2399 setUpNotifListener(); 2400 mListener.addTestPackage(TRAMPOLINE_APP_API_32); 2401 CountDownLatch postedLatch = mListener.setPostedCountDown(1); 2402 EventCallback callback = new EventCallback(); 2403 int notificationId = 6005; 2404 2405 // Post notification and fire its pending intent 2406 sendTrampolineMessage(TRAMPOLINE_SERVICE_API_32, MESSAGE_BROADCAST_NOTIFICATION, 2407 notificationId, callback); 2408 postedLatch.await(POST_TIMEOUT, TimeUnit.MILLISECONDS); 2409 StatusBarNotification statusBarNotification = mNotificationHelper.findPostedNotification( 2410 null, notificationId, SEARCH_TYPE.LISTENER); 2411 assertNotNull("Notification not posted on time", statusBarNotification); 2412 statusBarNotification.getNotification().contentIntent.send(); 2413 2414 assertTrue("Broadcast not received on time", 2415 callback.waitFor(EventCallback.BROADCAST_RECEIVED, TIMEOUT_LONG_MS)); 2416 assertTrue("Activity not started", 2417 callback.waitFor(EventCallback.ACTIVITY_STARTED, TIMEOUT_MS)); 2418 } 2419 testActivityStartOnServiceTrampoline_whenDefaultBrowser_isBlocked()2420 public void testActivityStartOnServiceTrampoline_whenDefaultBrowser_isBlocked() 2421 throws Exception { 2422 deactivateGracePeriod(); 2423 setDefaultBrowser(TRAMPOLINE_APP); 2424 setUpNotifListener(); 2425 mListener.addTestPackage(TRAMPOLINE_APP); 2426 CountDownLatch postedLatch = mListener.setPostedCountDown(1); 2427 EventCallback callback = new EventCallback(); 2428 int notificationId = 6006; 2429 2430 // Post notification and fire its pending intent 2431 sendTrampolineMessage(TRAMPOLINE_SERVICE, MESSAGE_SERVICE_NOTIFICATION, notificationId, 2432 callback); 2433 postedLatch.await(POST_TIMEOUT, TimeUnit.MILLISECONDS); 2434 StatusBarNotification statusBarNotification = mNotificationHelper.findPostedNotification( 2435 null, notificationId, SEARCH_TYPE.LISTENER); 2436 assertNotNull("Notification not posted on time", statusBarNotification); 2437 statusBarNotification.getNotification().contentIntent.send(); 2438 2439 assertTrue("Service not started on time", 2440 callback.waitFor(EventCallback.SERVICE_STARTED, TIMEOUT_MS)); 2441 assertFalse("Activity started", 2442 callback.waitFor(EventCallback.ACTIVITY_STARTED, TIMEOUT_MS)); 2443 } 2444 testActivityStartOnServiceTrampoline_whenDefaultBrowserApi32_isAllowed()2445 public void testActivityStartOnServiceTrampoline_whenDefaultBrowserApi32_isAllowed() 2446 throws Exception { 2447 deactivateGracePeriod(); 2448 setDefaultBrowser(TRAMPOLINE_APP_API_32); 2449 setUpNotifListener(); 2450 mListener.addTestPackage(TRAMPOLINE_APP_API_32); 2451 CountDownLatch postedLatch = mListener.setPostedCountDown(1); 2452 EventCallback callback = new EventCallback(); 2453 int notificationId = 6006; 2454 2455 // Post notification and fire its pending intent 2456 sendTrampolineMessage(TRAMPOLINE_SERVICE_API_32, MESSAGE_SERVICE_NOTIFICATION, 2457 notificationId, callback); 2458 postedLatch.await(POST_TIMEOUT, TimeUnit.MILLISECONDS); 2459 StatusBarNotification statusBarNotification = mNotificationHelper.findPostedNotification( 2460 null, notificationId, SEARCH_TYPE.LISTENER); 2461 assertNotNull("Notification not posted on time", statusBarNotification); 2462 statusBarNotification.getNotification().contentIntent.send(); 2463 2464 assertTrue("Service not started on time", 2465 callback.waitFor(EventCallback.SERVICE_STARTED, TIMEOUT_MS)); 2466 assertTrue("Activity not started", 2467 callback.waitFor(EventCallback.ACTIVITY_STARTED, TIMEOUT_MS)); 2468 } 2469 testGrantRevokeNotificationManagerApis_works()2470 public void testGrantRevokeNotificationManagerApis_works() { 2471 SystemUtil.runWithShellPermissionIdentity(() -> { 2472 ComponentName componentName = 2473 new ComponentName(STUB_PACKAGE_NAME, TestNotificationListener.class.getName()); 2474 mNotificationManager.setNotificationListenerAccessGranted( 2475 componentName, true, true); 2476 2477 assertThat( 2478 mNotificationManager.getEnabledNotificationListeners(), 2479 hasItem(componentName)); 2480 2481 mNotificationManager.setNotificationListenerAccessGranted( 2482 componentName, false, false); 2483 2484 assertThat( 2485 "Non-user-set changes should not override user-set", 2486 mNotificationManager.getEnabledNotificationListeners(), 2487 hasItem(componentName)); 2488 }); 2489 } 2490 testGrantRevokeNotificationManagerApis_exclusiveToPermissionController()2491 public void testGrantRevokeNotificationManagerApis_exclusiveToPermissionController() { 2492 List<PackageInfo> allPackages = mPackageManager.getInstalledPackages( 2493 PackageManager.MATCH_DISABLED_COMPONENTS 2494 | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS); 2495 List<String> allowedPackages = Arrays.asList( 2496 mPackageManager.getPermissionControllerPackageName(), 2497 "com.android.shell"); 2498 for (PackageInfo pkg : allPackages) { 2499 if (!pkg.applicationInfo.isSystemApp() 2500 && mPackageManager.checkPermission( 2501 Manifest.permission.MANAGE_NOTIFICATION_LISTENERS, pkg.packageName) 2502 == PackageManager.PERMISSION_GRANTED 2503 && !allowedPackages.contains(pkg.packageName)) { 2504 fail(pkg.packageName + " can't hold " 2505 + Manifest.permission.MANAGE_NOTIFICATION_LISTENERS); 2506 } 2507 } 2508 } 2509 testChannelDeletion_cancelReason()2510 public void testChannelDeletion_cancelReason() throws Exception { 2511 setUpNotifListener(); 2512 CountDownLatch notificationPostedLatch = mListener.setPostedCountDown(1); 2513 2514 sendNotification(566, R.drawable.black); 2515 // wait for notification listener to receive notification 2516 notificationPostedLatch.await(500, TimeUnit.MILLISECONDS); 2517 assertEquals(1, mListener.mPosted.size()); 2518 String key = mListener.mPosted.get(0).getKey(); 2519 2520 mNotificationManager.deleteNotificationChannel(NOTIFICATION_CHANNEL_ID); 2521 2522 assertEquals(NotificationListenerService.REASON_CHANNEL_REMOVED, 2523 getCancellationReason(key)); 2524 } 2525 testMediaStyleRemotePlayback_noPermission()2526 public void testMediaStyleRemotePlayback_noPermission() throws Exception { 2527 int id = 99; 2528 final String deviceName = "device name"; 2529 final int deviceIcon = 123; 2530 final PendingIntent deviceIntent = getPendingIntent(); 2531 final Notification notification = 2532 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 2533 .setSmallIcon(R.drawable.black) 2534 .setStyle(new Notification.MediaStyle() 2535 .setRemotePlaybackInfo(deviceName, deviceIcon, deviceIntent)) 2536 .build(); 2537 mNotificationManager.notify(id, notification); 2538 2539 StatusBarNotification sbn = mNotificationHelper.findPostedNotification(null, id, 2540 SEARCH_TYPE.APP); 2541 assertNotNull(sbn); 2542 2543 assertFalse(sbn.getNotification().extras 2544 .containsKey(Notification.EXTRA_MEDIA_REMOTE_DEVICE)); 2545 assertFalse(sbn.getNotification().extras 2546 .containsKey(Notification.EXTRA_MEDIA_REMOTE_ICON)); 2547 assertFalse(sbn.getNotification().extras 2548 .containsKey(Notification.EXTRA_MEDIA_REMOTE_INTENT)); 2549 } 2550 testMediaStyleRemotePlayback_hasPermission()2551 public void testMediaStyleRemotePlayback_hasPermission() throws Exception { 2552 int id = 99; 2553 final String deviceName = "device name"; 2554 final int deviceIcon = 123; 2555 final PendingIntent deviceIntent = getPendingIntent(); 2556 final Notification notification = 2557 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 2558 .setSmallIcon(R.drawable.black) 2559 .setStyle(new Notification.MediaStyle() 2560 .setRemotePlaybackInfo(deviceName, deviceIcon, deviceIntent)) 2561 .build(); 2562 2563 SystemUtil.runWithShellPermissionIdentity(() -> { 2564 mNotificationManager.notify(id, notification); 2565 }, android.Manifest.permission.MEDIA_CONTENT_CONTROL); 2566 2567 StatusBarNotification sbn = mNotificationHelper.findPostedNotification( 2568 null, id, SEARCH_TYPE.APP); 2569 assertNotNull(sbn); 2570 assertEquals(deviceName, sbn.getNotification().extras 2571 .getString(Notification.EXTRA_MEDIA_REMOTE_DEVICE)); 2572 assertEquals(deviceIcon, sbn.getNotification().extras 2573 .getInt(Notification.EXTRA_MEDIA_REMOTE_ICON)); 2574 assertEquals(deviceIntent, sbn.getNotification().extras 2575 .getParcelable(Notification.EXTRA_MEDIA_REMOTE_INTENT)); 2576 } 2577 testCustomMediaStyleRemotePlayback_noPermission()2578 public void testCustomMediaStyleRemotePlayback_noPermission() throws Exception { 2579 int id = 99; 2580 final String deviceName = "device name"; 2581 final int deviceIcon = 123; 2582 final PendingIntent deviceIntent = getPendingIntent(); 2583 final Notification notification = 2584 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 2585 .setSmallIcon(R.drawable.black) 2586 .setStyle(new Notification.DecoratedMediaCustomViewStyle() 2587 .setRemotePlaybackInfo(deviceName, deviceIcon, deviceIntent)) 2588 .build(); 2589 mNotificationManager.notify(id, notification); 2590 2591 StatusBarNotification sbn = mNotificationHelper.findPostedNotification( 2592 null, id, SEARCH_TYPE.APP); 2593 assertNotNull(sbn); 2594 2595 assertFalse(sbn.getNotification().extras 2596 .containsKey(Notification.EXTRA_MEDIA_REMOTE_DEVICE)); 2597 assertFalse(sbn.getNotification().extras 2598 .containsKey(Notification.EXTRA_MEDIA_REMOTE_ICON)); 2599 assertFalse(sbn.getNotification().extras 2600 .containsKey(Notification.EXTRA_MEDIA_REMOTE_INTENT)); 2601 } 2602 testCustomMediaStyleRemotePlayback_hasPermission()2603 public void testCustomMediaStyleRemotePlayback_hasPermission() throws Exception { 2604 int id = 99; 2605 final String deviceName = "device name"; 2606 final int deviceIcon = 123; 2607 final PendingIntent deviceIntent = getPendingIntent(); 2608 final Notification notification = 2609 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 2610 .setSmallIcon(R.drawable.black) 2611 .setStyle(new Notification.DecoratedMediaCustomViewStyle() 2612 .setRemotePlaybackInfo(deviceName, deviceIcon, deviceIntent)) 2613 .build(); 2614 2615 SystemUtil.runWithShellPermissionIdentity(() -> { 2616 mNotificationManager.notify(id, notification); 2617 }, android.Manifest.permission.MEDIA_CONTENT_CONTROL); 2618 2619 StatusBarNotification sbn = mNotificationHelper.findPostedNotification( 2620 null, id, SEARCH_TYPE.APP); 2621 assertNotNull(sbn); 2622 assertEquals(deviceName, sbn.getNotification().extras 2623 .getString(Notification.EXTRA_MEDIA_REMOTE_DEVICE)); 2624 assertEquals(deviceIcon, sbn.getNotification().extras 2625 .getInt(Notification.EXTRA_MEDIA_REMOTE_ICON)); 2626 assertEquals(deviceIntent, sbn.getNotification().extras 2627 .getParcelable(Notification.EXTRA_MEDIA_REMOTE_INTENT)); 2628 } 2629 testNoPermission()2630 public void testNoPermission() throws Exception { 2631 int id = 7; 2632 SystemUtil.runWithShellPermissionIdentity( 2633 () -> mContext.getSystemService(PermissionManager.class) 2634 .revokePostNotificationPermissionWithoutKillForTest( 2635 mContext.getPackageName(), 2636 android.os.Process.myUserHandle().getIdentifier()), 2637 REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL, 2638 REVOKE_RUNTIME_PERMISSIONS); 2639 2640 final Notification notification = 2641 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 2642 .setSmallIcon(R.drawable.black) 2643 .build(); 2644 mNotificationManager.notify(id, notification); 2645 2646 assertTrue(mNotificationHelper.isNotificationGone(id, SEARCH_TYPE.APP)); 2647 } 2648 testIsAmbient()2649 public void testIsAmbient() throws Exception { 2650 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 2651 assertNotNull(mListener); 2652 CountDownLatch postedLatch = mListener.setPostedCountDown(2); 2653 2654 NotificationChannel lowChannel = new NotificationChannel( 2655 "testIsAmbientLOW", "testIsAmbientLOW", IMPORTANCE_LOW); 2656 NotificationChannel minChannel = new NotificationChannel( 2657 "testIsAmbientMIN", "testIsAmbientMIN", IMPORTANCE_MIN); 2658 mNotificationManager.createNotificationChannel(lowChannel); 2659 mNotificationManager.createNotificationChannel(minChannel); 2660 2661 final Notification lowN = 2662 new Notification.Builder(mContext, lowChannel.getId()) 2663 .setSmallIcon(R.drawable.black) 2664 .build(); 2665 final Notification minN = 2666 new Notification.Builder(mContext, minChannel.getId()) 2667 .setSmallIcon(R.drawable.black) 2668 .build(); 2669 mNotificationManager.notify("lowN", 1, lowN); 2670 mNotificationManager.notify("minN", 1, minN); 2671 2672 postedLatch.await(POST_TIMEOUT, TimeUnit.MILLISECONDS); 2673 StatusBarNotification lowSbn = mNotificationHelper.findPostedNotification("lowN", 1, 2674 SEARCH_TYPE.POSTED); 2675 StatusBarNotification minSbn = mNotificationHelper.findPostedNotification("minN", 1, 2676 SEARCH_TYPE.POSTED); 2677 2678 NotificationListenerService.RankingMap rankingMap = mListener.mRankingMap; 2679 NotificationListenerService.Ranking outRanking = new NotificationListenerService.Ranking(); 2680 2681 rankingMap.getRanking(lowSbn.getKey(), outRanking); 2682 assertFalse(outRanking.isAmbient()); 2683 2684 rankingMap.getRanking(minSbn.getKey(), outRanking); 2685 assertEquals(outRanking.getKey(), IMPORTANCE_MIN, outRanking.getChannel().getImportance()); 2686 assertTrue(outRanking.isAmbient()); 2687 } 2688 testFlagForegroundServiceNeedsRealFgs()2689 public void testFlagForegroundServiceNeedsRealFgs() throws Exception { 2690 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 2691 assertNotNull(mListener); 2692 CountDownLatch postedLatch = mListener.setPostedCountDown(1); 2693 2694 final Notification n = 2695 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 2696 .setSmallIcon(R.drawable.black) 2697 .setFlag(FLAG_FOREGROUND_SERVICE, true) 2698 .build(); 2699 mNotificationManager.notify("testFlagForegroundServiceNeedsRealFgs", 1, n); 2700 postedLatch.await(POST_TIMEOUT, TimeUnit.MILLISECONDS); 2701 StatusBarNotification sbn = mNotificationHelper.findPostedNotification( 2702 "testFlagForegroundServiceNeedsRealFgs", 1, SEARCH_TYPE.POSTED); 2703 2704 assertEquals(0, (sbn.getNotification().flags & FLAG_FOREGROUND_SERVICE)); 2705 } 2706 testFlagUserInitiatedJobNeedsRealUij()2707 public void testFlagUserInitiatedJobNeedsRealUij() throws Exception { 2708 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 2709 assertNotNull(mListener); 2710 CountDownLatch postedLatch = mListener.setPostedCountDown(1); 2711 2712 final Notification n = 2713 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 2714 .setSmallIcon(R.drawable.black) 2715 .setFlag(FLAG_USER_INITIATED_JOB, true) 2716 .build(); 2717 mNotificationManager.notify("testFlagUserInitiatedJobNeedsRealUij", 1, n); 2718 postedLatch.await(POST_TIMEOUT, TimeUnit.MILLISECONDS); 2719 StatusBarNotification sbn = mNotificationHelper.findPostedNotification( 2720 "testFlagUserInitiatedJobNeedsRealUij", 1, SEARCH_TYPE.POSTED); 2721 2722 assertFalse(sbn.getNotification().isUserInitiatedJob()); 2723 } 2724 2725 private static class EventCallback extends Handler { 2726 private static final int BROADCAST_RECEIVED = 1; 2727 private static final int SERVICE_STARTED = 2; 2728 private static final int ACTIVITY_STARTED = 3; 2729 private static final int NOTIFICATION_CLICKED = 4; 2730 2731 private final Map<Integer, CompletableFuture<Integer>> mEvents = 2732 Collections.synchronizedMap(new ArrayMap<>()); 2733 EventCallback()2734 private EventCallback() { 2735 super(Looper.getMainLooper()); 2736 } 2737 2738 @Override handleMessage(Message message)2739 public void handleMessage(Message message) { 2740 mEvents.computeIfAbsent(message.what, e -> new CompletableFuture<>()).obtrudeValue( 2741 message.arg1); 2742 } 2743 waitFor(int event, long timeoutMs)2744 public boolean waitFor(int event, long timeoutMs) { 2745 try { 2746 return mEvents.computeIfAbsent(event, e -> new CompletableFuture<>()).get(timeoutMs, 2747 TimeUnit.MILLISECONDS) == 0; 2748 } catch (InterruptedException | ExecutionException | TimeoutException e) { 2749 return false; 2750 } 2751 } 2752 } 2753 } 2754