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_NO_CLEAR; 28 import static android.app.Notification.FLAG_USER_INITIATED_JOB; 29 import static android.app.NotificationManager.IMPORTANCE_DEFAULT; 30 import static android.app.NotificationManager.IMPORTANCE_HIGH; 31 import static android.app.NotificationManager.IMPORTANCE_LOW; 32 import static android.app.NotificationManager.IMPORTANCE_MIN; 33 import static android.app.NotificationManager.IMPORTANCE_NONE; 34 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; 35 import static android.content.pm.PackageManager.FEATURE_WATCH; 36 import static android.service.notification.NotificationListenerService.META_DATA_DEFAULT_AUTOBIND; 37 38 import static com.google.common.truth.Truth.assertThat; 39 40 import static org.hamcrest.CoreMatchers.hasItem; 41 import static org.junit.Assert.assertEquals; 42 import static org.junit.Assert.assertFalse; 43 import static org.junit.Assert.assertNotNull; 44 import static org.junit.Assert.assertNull; 45 import static org.junit.Assert.assertThat; 46 import static org.junit.Assert.assertTrue; 47 import static org.junit.Assert.fail; 48 49 import android.Manifest; 50 import android.app.Notification; 51 import android.app.NotificationChannel; 52 import android.app.NotificationChannelGroup; 53 import android.app.NotificationManager; 54 import android.app.NotificationManager.CallNotificationEventListener; 55 import android.app.PendingIntent; 56 import android.app.compat.CompatChanges; 57 import android.app.role.RoleManager; 58 import android.app.stubs.GetResultActivity; 59 import android.app.stubs.R; 60 import android.app.stubs.shared.FutureServiceConnection; 61 import android.app.stubs.shared.NotificationHelper.SEARCH_TYPE; 62 import android.app.stubs.shared.TestNotificationListener; 63 import android.content.ComponentName; 64 import android.content.ContentResolver; 65 import android.content.Context; 66 import android.content.Intent; 67 import android.content.ServiceConnection; 68 import android.content.pm.PackageInfo; 69 import android.content.pm.PackageManager; 70 import android.content.pm.ServiceInfo; 71 import android.content.res.AssetFileDescriptor; 72 import android.graphics.Bitmap; 73 import android.graphics.drawable.Icon; 74 import android.media.AudioAttributes; 75 import android.media.session.MediaSession; 76 import android.net.Uri; 77 import android.os.Build; 78 import android.os.Handler; 79 import android.os.IBinder; 80 import android.os.Looper; 81 import android.os.Message; 82 import android.os.Messenger; 83 import android.os.SystemProperties; 84 import android.os.UserHandle; 85 import android.permission.PermissionManager; 86 import android.permission.cts.PermissionUtils; 87 import android.platform.test.annotations.AsbSecurityTest; 88 import android.platform.test.annotations.RequiresDevice; 89 import android.platform.test.annotations.RequiresFlagsDisabled; 90 import android.platform.test.annotations.RequiresFlagsEnabled; 91 import android.provider.Settings; 92 import android.service.notification.Flags; 93 import android.service.notification.NotificationListenerService; 94 import android.service.notification.StatusBarNotification; 95 import android.util.ArrayMap; 96 import android.util.Log; 97 import android.widget.RemoteViews; 98 99 import androidx.annotation.NonNull; 100 import androidx.annotation.Nullable; 101 import androidx.test.filters.LargeTest; 102 import androidx.test.platform.app.InstrumentationRegistry; 103 import androidx.test.runner.AndroidJUnit4; 104 import androidx.test.uiautomator.UiDevice; 105 106 import com.android.compatibility.common.util.PollingCheck; 107 import com.android.compatibility.common.util.SystemUtil; 108 import com.android.compatibility.common.util.ThrowingSupplier; 109 import com.android.modules.utils.build.SdkLevel; 110 import com.android.test.notificationlistener.INLSControlService; 111 import com.android.test.notificationlistener.INotificationUriAccessService; 112 113 import com.google.common.base.Preconditions; 114 115 import org.junit.After; 116 import org.junit.Before; 117 import org.junit.FixMethodOrder; 118 import org.junit.Test; 119 import org.junit.runner.RunWith; 120 import org.junit.runners.MethodSorters; 121 122 import java.io.BufferedReader; 123 import java.io.IOException; 124 import java.io.InputStreamReader; 125 import java.util.ArrayList; 126 import java.util.Arrays; 127 import java.util.Collections; 128 import java.util.HashMap; 129 import java.util.List; 130 import java.util.Map; 131 import java.util.UUID; 132 import java.util.concurrent.CompletableFuture; 133 import java.util.concurrent.CompletionException; 134 import java.util.concurrent.CountDownLatch; 135 import java.util.concurrent.ExecutionException; 136 import java.util.concurrent.Executor; 137 import java.util.concurrent.Semaphore; 138 import java.util.concurrent.TimeUnit; 139 import java.util.concurrent.TimeoutException; 140 141 /* This tests NotificationListenerService together with NotificationManager, as you need to have 142 * notifications to manipulate in order to test the listener service. */ 143 @RunWith(AndroidJUnit4.class) 144 @FixMethodOrder(MethodSorters.NAME_ASCENDING) 145 public class NotificationManagerTest extends BaseNotificationManagerTest { 146 public static final String NOTIFICATIONPROVIDER = "com.android.test.notificationprovider"; 147 public static final String RICH_NOTIFICATION_ACTIVITY = 148 "com.android.test.notificationprovider.RichNotificationActivity"; 149 final String TAG = NotificationManagerTest.class.getSimpleName(); 150 final boolean DEBUG = false; 151 152 private static final long ENFORCE_NO_CLEAR_FLAG_ON_MEDIA_NOTIFICATION = 264179692L; 153 private static final String DELEGATE_POST_CLASS = TEST_APP + ".NotificationDelegateAndPost"; 154 private static final String REVOKE_CLASS = TEST_APP + ".NotificationRevoker"; 155 156 private static final String TRAMPOLINE_APP = 157 "com.android.test.notificationtrampoline.current"; 158 private static final String TRAMPOLINE_APP_API_30 = 159 "com.android.test.notificationtrampoline.api30"; 160 private static final String TRAMPOLINE_APP_API_32 = 161 "com.android.test.notificationtrampoline.api32"; 162 private static final ComponentName TRAMPOLINE_SERVICE = 163 new ComponentName(TRAMPOLINE_APP, 164 "com.android.test.notificationtrampoline.NotificationTrampolineTestService"); 165 private static final ComponentName TRAMPOLINE_SERVICE_API_30 = 166 new ComponentName(TRAMPOLINE_APP_API_30, 167 "com.android.test.notificationtrampoline.NotificationTrampolineTestService"); 168 private static final ComponentName TRAMPOLINE_SERVICE_API_32 = 169 new ComponentName(TRAMPOLINE_APP_API_32, 170 "com.android.test.notificationtrampoline.NotificationTrampolineTestService"); 171 172 private static final String PRESSURE_APP_00 = 173 "com.android.test.NotificationPressure00"; 174 private static final String PRESSURE_APP_01 = 175 "com.android.test.NotificationPressure01"; 176 private static final String PRESSURE_APP_02 = 177 "com.android.test.NotificationPressure02"; 178 private static final String PRESSURE_APP_03 = 179 "com.android.test.NotificationPressure03"; 180 private static final String PRESSURE_APP_04 = 181 "com.android.test.NotificationPressure04"; 182 private static final String PRESSURE_APP_05 = 183 "com.android.test.NotificationPressure05"; 184 private static final String PRESSURE_APP_06 = 185 "com.android.test.NotificationPressure06"; 186 private static final String PRESSURE_APP_07 = 187 "com.android.test.NotificationPressure07"; 188 189 private static final ComponentName PRESSURE_SERVICE_00 = new ComponentName(PRESSURE_APP_00, 190 "com.android.test.notificationtrampoline.NotificationTrampolineTestService"); 191 private static final ComponentName PRESSURE_SERVICE_01 = new ComponentName(PRESSURE_APP_01, 192 "com.android.test.notificationtrampoline.NotificationTrampolineTestService"); 193 private static final ComponentName PRESSURE_SERVICE_02 = new ComponentName(PRESSURE_APP_02, 194 "com.android.test.notificationtrampoline.NotificationTrampolineTestService"); 195 private static final ComponentName PRESSURE_SERVICE_03 = new ComponentName(PRESSURE_APP_03, 196 "com.android.test.notificationtrampoline.NotificationTrampolineTestService"); 197 private static final ComponentName PRESSURE_SERVICE_04 = new ComponentName(PRESSURE_APP_04, 198 "com.android.test.notificationtrampoline.NotificationTrampolineTestService"); 199 private static final ComponentName PRESSURE_SERVICE_05 = new ComponentName(PRESSURE_APP_05, 200 "com.android.test.notificationtrampoline.NotificationTrampolineTestService"); 201 private static final ComponentName PRESSURE_SERVICE_06 = new ComponentName(PRESSURE_APP_06, 202 "com.android.test.notificationtrampoline.NotificationTrampolineTestService"); 203 private static final ComponentName PRESSURE_SERVICE_07 = new ComponentName(PRESSURE_APP_07, 204 "com.android.test.notificationtrampoline.NotificationTrampolineTestService"); 205 206 207 private static final ComponentName URI_ACCESS_SERVICE = new ComponentName( 208 "com.android.test.notificationlistener", 209 "com.android.test.notificationlistener.NotificationUriAccessService"); 210 211 private static final ComponentName NLS_CONTROL_SERVICE = new ComponentName( 212 "com.android.test.notificationlistener", 213 "com.android.test.notificationlistener.NLSControlService"); 214 215 private static final ComponentName NO_AUTOBIND_NLS = new ComponentName( 216 "com.android.test.notificationlistener", 217 "com.android.test.notificationlistener.TestNotificationListenerNoAutobind"); 218 219 private static final String STUB_PACKAGE_NAME = "android.app.stubs"; 220 221 private static final long TIMEOUT_LONG_MS = 10000; 222 private static final long TIMEOUT_MS = 4000; 223 224 private static final long POST_TIMEOUT = 200; 225 private static final int MESSAGE_BROADCAST_NOTIFICATION = 1; 226 private static final int MESSAGE_SERVICE_NOTIFICATION = 2; 227 private static final int MESSAGE_CLICK_NOTIFICATION = 3; 228 private static final int MESSAGE_CANCEL_ALL_NOTIFICATIONS = 4; 229 230 private String mId; 231 private INotificationUriAccessService mNotificationUriAccessService; 232 private INLSControlService mNLSControlService; 233 private FutureServiceConnection mTrampolineConnection; 234 235 @Nullable 236 private List<String> mPreviousDefaultBrowser; 237 238 @Before setUp()239 public void setUp() throws Exception { 240 PermissionUtils.grantPermission(mContext.getPackageName(), POST_NOTIFICATIONS); 241 PermissionUtils.grantPermission(STUB_PACKAGE_NAME, POST_NOTIFICATIONS); 242 PermissionUtils.grantPermission(TEST_APP, POST_NOTIFICATIONS); 243 PermissionUtils.grantPermission(TRAMPOLINE_APP, POST_NOTIFICATIONS); 244 PermissionUtils.grantPermission(TRAMPOLINE_APP_API_30, POST_NOTIFICATIONS); 245 PermissionUtils.grantPermission(TRAMPOLINE_APP_API_32, POST_NOTIFICATIONS); 246 PermissionUtils.grantPermission(NOTIFICATIONPROVIDER, POST_NOTIFICATIONS); 247 PermissionUtils.grantPermission(PRESSURE_APP_00, POST_NOTIFICATIONS); 248 PermissionUtils.grantPermission(PRESSURE_APP_01, POST_NOTIFICATIONS); 249 PermissionUtils.grantPermission(PRESSURE_APP_02, POST_NOTIFICATIONS); 250 PermissionUtils.grantPermission(PRESSURE_APP_03, POST_NOTIFICATIONS); 251 PermissionUtils.grantPermission(PRESSURE_APP_04, POST_NOTIFICATIONS); 252 PermissionUtils.grantPermission(PRESSURE_APP_05, POST_NOTIFICATIONS); 253 PermissionUtils.grantPermission(PRESSURE_APP_06, POST_NOTIFICATIONS); 254 PermissionUtils.grantPermission(PRESSURE_APP_07, POST_NOTIFICATIONS); 255 PermissionUtils.setAppOp(mContext.getPackageName(), 256 android.Manifest.permission.ACCESS_NOTIFICATIONS, MODE_ALLOWED); 257 258 // This will leave a set of channels on the device with each test run. 259 mId = UUID.randomUUID().toString(); 260 261 // delay between tests so notifications aren't dropped by the rate limiter 262 try { 263 Thread.sleep(500); 264 } catch (InterruptedException e) { 265 } 266 } 267 268 @After tearDown()269 public void tearDown() throws Exception { 270 // For trampoline tests 271 if (mTrampolineConnection != null) { 272 mContext.unbindService(mTrampolineConnection); 273 mTrampolineConnection = null; 274 } 275 if (mListener != null) { 276 mListener.removeTestPackage(TRAMPOLINE_APP_API_30); 277 mListener.removeTestPackage(TRAMPOLINE_APP); 278 } 279 if (mPreviousDefaultBrowser != null) { 280 restoreDefaultBrowser(); 281 } 282 283 // Use test API to prevent PermissionManager from killing the test process when revoking 284 // permission. 285 SystemUtil.runWithShellPermissionIdentity( 286 () -> mContext.getSystemService(PermissionManager.class) 287 .revokePostNotificationPermissionWithoutKillForTest( 288 mContext.getPackageName(), 289 android.os.Process.myUserHandle().getIdentifier()), 290 REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL, 291 REVOKE_RUNTIME_PERMISSIONS); 292 PermissionUtils.revokePermission(STUB_PACKAGE_NAME, POST_NOTIFICATIONS); 293 PermissionUtils.revokePermission(TEST_APP, POST_NOTIFICATIONS); 294 PermissionUtils.revokePermission(TRAMPOLINE_APP, POST_NOTIFICATIONS); 295 PermissionUtils.revokePermission(NOTIFICATIONPROVIDER, POST_NOTIFICATIONS); 296 PermissionUtils.revokePermission(PRESSURE_APP_00, POST_NOTIFICATIONS); 297 PermissionUtils.revokePermission(PRESSURE_APP_01, POST_NOTIFICATIONS); 298 PermissionUtils.revokePermission(PRESSURE_APP_02, POST_NOTIFICATIONS); 299 PermissionUtils.revokePermission(PRESSURE_APP_03, POST_NOTIFICATIONS); 300 PermissionUtils.revokePermission(PRESSURE_APP_04, POST_NOTIFICATIONS); 301 PermissionUtils.revokePermission(PRESSURE_APP_05, POST_NOTIFICATIONS); 302 PermissionUtils.revokePermission(PRESSURE_APP_06, POST_NOTIFICATIONS); 303 PermissionUtils.revokePermission(PRESSURE_APP_07, POST_NOTIFICATIONS); 304 PermissionUtils.setAppOp(mContext.getPackageName(), 305 android.Manifest.permission.ACCESS_NOTIFICATIONS, MODE_DEFAULT); 306 } 307 getPendingIntent()308 private PendingIntent getPendingIntent() { 309 return PendingIntent.getActivity( 310 mContext, 0, new Intent(mContext, this.getClass()), 311 PendingIntent.FLAG_MUTABLE_UNAUDITED); 312 } 313 isGroupSummary(Notification n)314 private boolean isGroupSummary(Notification n) { 315 return n.getGroup() != null && (n.flags & Notification.FLAG_GROUP_SUMMARY) != 0; 316 } 317 assertOnlySomeNotificationsAutogrouped(List<Integer> autoGroupedIds)318 private void assertOnlySomeNotificationsAutogrouped(List<Integer> autoGroupedIds) 319 throws Exception { 320 String expectedGroupKey = null; 321 Thread.sleep(150); 322 323 StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications(); 324 for (StatusBarNotification sbn : sbns) { 325 if (isGroupSummary(sbn.getNotification()) 326 || autoGroupedIds.contains(sbn.getId())) { 327 assertTrue(sbn.getKey() + " is unexpectedly not autogrouped", 328 sbn.getOverrideGroupKey() != null); 329 if (expectedGroupKey == null) { 330 expectedGroupKey = sbn.getGroupKey(); 331 } 332 assertEquals(expectedGroupKey, sbn.getGroupKey()); 333 } else { 334 assertTrue(sbn.isGroup()); 335 assertTrue(sbn.getKey() + " is unexpectedly autogrouped,", 336 sbn.getOverrideGroupKey() == null); 337 assertTrue(sbn.getKey() + " has an unusual group key", 338 sbn.getGroupKey() != expectedGroupKey); 339 } 340 } 341 } 342 assertAllPostedNotificationsAutogrouped()343 private void assertAllPostedNotificationsAutogrouped() throws Exception { 344 String expectedGroupKey = null; 345 // Posting can take ~100 ms 346 Thread.sleep(150); 347 348 StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications(); 349 for (StatusBarNotification sbn : sbns) { 350 // all notis should be in a group determined by autogrouping 351 assertTrue(sbn.getOverrideGroupKey() != null); 352 if (expectedGroupKey == null) { 353 expectedGroupKey = sbn.getGroupKey(); 354 } 355 // all notis should be in the same group 356 assertEquals(expectedGroupKey, sbn.getGroupKey()); 357 } 358 } 359 getCancellationReason(String key)360 private int getCancellationReason(String key) { 361 for (int tries = 3; tries-- > 0; ) { 362 if (mListener.mRemovedReasons.containsKey(key)) { 363 return mListener.mRemovedReasons.get(key); 364 } 365 try { 366 Thread.sleep(1000); 367 } catch (InterruptedException ex) { 368 // pass 369 } 370 } 371 return -1; 372 } 373 getAssistantCancellationReason(String key)374 private int getAssistantCancellationReason(String key) { 375 for (int tries = 3; tries-- > 0; ) { 376 if (mAssistant.mRemoved.containsKey(key)) { 377 return mAssistant.mRemoved.get(key); 378 } 379 try { 380 Thread.sleep(1000); 381 } catch (InterruptedException ex) { 382 // pass 383 } 384 } 385 return -1; 386 } 387 assertNotificationCount(int expectedCount)388 private void assertNotificationCount(int expectedCount) { 389 // notification is a bit asynchronous so it may take a few ms to appear in 390 // getActiveNotifications() 391 // we will check for it for up to 400ms before giving up 392 int lastCount = 0; 393 for (int tries = 4; tries-- > 0; ) { 394 final StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications(); 395 lastCount = sbns.length; 396 if (expectedCount == lastCount) return; 397 try { 398 Thread.sleep(100); 399 } catch (InterruptedException ex) { 400 // pass 401 } 402 } 403 fail("Expected " + expectedCount + " posted notifications, were " + lastCount); 404 } 405 compareChannels(NotificationChannel expected, NotificationChannel actual)406 private void compareChannels(NotificationChannel expected, NotificationChannel actual) { 407 if (actual == null) { 408 fail("actual channel is null"); 409 return; 410 } 411 if (expected == null) { 412 fail("expected channel is null"); 413 return; 414 } 415 assertEquals(expected.getId(), actual.getId()); 416 assertEquals(expected.getName().toString(), actual.getName().toString()); 417 assertEquals(expected.getDescription(), actual.getDescription()); 418 assertEquals(expected.shouldVibrate(), actual.shouldVibrate()); 419 assertEquals(expected.shouldShowLights(), actual.shouldShowLights()); 420 assertEquals(expected.getLightColor(), actual.getLightColor()); 421 assertEquals(expected.getImportance(), actual.getImportance()); 422 if (expected.getSound() == null) { 423 assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actual.getSound()); 424 assertEquals(Notification.AUDIO_ATTRIBUTES_DEFAULT, actual.getAudioAttributes()); 425 } else { 426 assertEquals(expected.getSound(), actual.getSound()); 427 assertEquals(expected.getAudioAttributes(), actual.getAudioAttributes()); 428 } 429 assertTrue(Arrays.equals(expected.getVibrationPattern(), actual.getVibrationPattern())); 430 if (android.app.Flags.notificationChannelVibrationEffectApi()) { 431 assertEquals(expected.getVibrationEffect(), actual.getVibrationEffect()); 432 } 433 assertEquals(expected.getGroup(), actual.getGroup()); 434 assertEquals(expected.getConversationId(), actual.getConversationId()); 435 assertEquals(expected.getParentChannelId(), actual.getParentChannelId()); 436 assertEquals(expected.isDemoted(), actual.isDemoted()); 437 } 438 sendTrampolineMessage(ComponentName component, int message, int notificationId, Handler callback)439 private void sendTrampolineMessage(ComponentName component, int message, 440 int notificationId, Handler callback) throws Exception { 441 if (mTrampolineConnection == null) { 442 Intent intent = new Intent(); 443 intent.setComponent(component); 444 mTrampolineConnection = new FutureServiceConnection(); 445 assertTrue( 446 mContext.bindService(intent, mTrampolineConnection, Context.BIND_AUTO_CREATE)); 447 } 448 Messenger service = new Messenger(mTrampolineConnection.get(TIMEOUT_MS)); 449 service.send(Message.obtain(null, message, notificationId, -1, new Messenger(callback))); 450 } 451 setDefaultBrowser(String packageName)452 private void setDefaultBrowser(String packageName) throws Exception { 453 UserHandle user = android.os.Process.myUserHandle(); 454 mPreviousDefaultBrowser = SystemUtil.callWithShellPermissionIdentity( 455 () -> mRoleManager.getRoleHoldersAsUser(RoleManager.ROLE_BROWSER, user)); 456 CompletableFuture<Boolean> set = new CompletableFuture<>(); 457 SystemUtil.runWithShellPermissionIdentity( 458 () -> mRoleManager.addRoleHolderAsUser(RoleManager.ROLE_BROWSER, packageName, 0, 459 user, mContext.getMainExecutor(), set::complete)); 460 assertTrue("Failed to set " + packageName + " as default browser", 461 set.get(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 462 } 463 restoreDefaultBrowser()464 private void restoreDefaultBrowser() throws Exception { 465 Preconditions.checkState(mPreviousDefaultBrowser != null); 466 UserHandle user = android.os.Process.myUserHandle(); 467 Executor executor = mContext.getMainExecutor(); 468 CompletableFuture<Boolean> restored = new CompletableFuture<>(); 469 SystemUtil.runWithShellPermissionIdentity(() -> { 470 mRoleManager.clearRoleHoldersAsUser(RoleManager.ROLE_BROWSER, 0, user, executor, 471 restored::complete); 472 for (String packageName : mPreviousDefaultBrowser) { 473 mRoleManager.addRoleHolderAsUser(RoleManager.ROLE_BROWSER, packageName, 474 0, user, executor, restored::complete); 475 } 476 }); 477 assertTrue("Failed to restore default browser", 478 restored.get(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 479 } 480 481 /** 482 * Previous tests could have started activities within the grace period, so go home to avoid 483 * allowing background activity starts due to this exemption. 484 */ deactivateGracePeriod()485 private void deactivateGracePeriod() { 486 UiDevice.getInstance(mInstrumentation).pressHome(); 487 } 488 verifyCanUseFullScreenIntent(int appOpState, boolean canSend)489 private void verifyCanUseFullScreenIntent(int appOpState, boolean canSend) throws Exception { 490 final int previousState = PermissionUtils.getAppOp(STUB_PACKAGE_NAME, 491 Manifest.permission.USE_FULL_SCREEN_INTENT); 492 try { 493 PermissionUtils.setAppOp(STUB_PACKAGE_NAME, 494 Manifest.permission.USE_FULL_SCREEN_INTENT, 495 appOpState); 496 497 if (canSend) { 498 assertTrue(mNotificationManager.canUseFullScreenIntent()); 499 } else { 500 assertFalse(mNotificationManager.canUseFullScreenIntent()); 501 } 502 503 } finally { 504 // Clean up by setting to app op to previous state. 505 PermissionUtils.setAppOp(STUB_PACKAGE_NAME, 506 Manifest.permission.USE_FULL_SCREEN_INTENT, 507 previousState); 508 } 509 } 510 511 @Test testCanSendFullScreenIntent_modeDefault_returnsIsPermissionGranted()512 public void testCanSendFullScreenIntent_modeDefault_returnsIsPermissionGranted() 513 throws Exception { 514 final boolean isPermissionGranted = PermissionUtils.isPermissionGranted(STUB_PACKAGE_NAME, 515 Manifest.permission.USE_FULL_SCREEN_INTENT); 516 verifyCanUseFullScreenIntent(MODE_DEFAULT, /*canSend=*/ isPermissionGranted); 517 } 518 519 @Test testCanSendFullScreenIntent_modeAllowed_returnsTrue()520 public void testCanSendFullScreenIntent_modeAllowed_returnsTrue() throws Exception { 521 verifyCanUseFullScreenIntent(MODE_ALLOWED, /*canSend=*/ true); 522 } 523 524 @Test testCanSendFullScreenIntent_modeErrored_returnsFalse()525 public void testCanSendFullScreenIntent_modeErrored_returnsFalse() throws Exception { 526 verifyCanUseFullScreenIntent(MODE_ERRORED, /*canSend=*/ false); 527 } 528 529 @Test testCreateChannelGroup()530 public void testCreateChannelGroup() throws Exception { 531 final NotificationChannelGroup ncg = new NotificationChannelGroup("a group", "a label"); 532 final NotificationChannel channel = 533 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 534 channel.setGroup(ncg.getId()); 535 mNotificationManager.createNotificationChannelGroup(ncg); 536 final NotificationChannel ungrouped = 537 new NotificationChannel(mId + "!", "name", IMPORTANCE_DEFAULT); 538 try { 539 mNotificationManager.createNotificationChannel(channel); 540 mNotificationManager.createNotificationChannel(ungrouped); 541 542 List<NotificationChannelGroup> ncgs = 543 mNotificationManager.getNotificationChannelGroups(); 544 assertEquals(1, ncgs.size()); 545 assertEquals(ncg.getName(), ncgs.get(0).getName().toString()); 546 assertEquals(ncg.getDescription(), ncgs.get(0).getDescription()); 547 assertEquals(channel.getId(), ncgs.get(0).getChannels().get(0).getId()); 548 } finally { 549 mNotificationManager.deleteNotificationChannelGroup(ncg.getId()); 550 } 551 } 552 553 @Test testGetChannelGroup()554 public void testGetChannelGroup() throws Exception { 555 final NotificationChannelGroup ncg = new NotificationChannelGroup("a group", "a label"); 556 ncg.setDescription("bananas"); 557 final NotificationChannelGroup ncg2 = new NotificationChannelGroup("group 2", "label 2"); 558 final NotificationChannel channel = 559 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 560 channel.setGroup(ncg.getId()); 561 562 mNotificationManager.createNotificationChannelGroup(ncg); 563 mNotificationManager.createNotificationChannelGroup(ncg2); 564 mNotificationManager.createNotificationChannel(channel); 565 566 NotificationChannelGroup actual = 567 mNotificationManager.getNotificationChannelGroup(ncg.getId()); 568 assertEquals(ncg.getId(), actual.getId()); 569 assertEquals(ncg.getName(), actual.getName().toString()); 570 assertEquals(ncg.getDescription(), actual.getDescription()); 571 assertEquals(channel.getId(), actual.getChannels().get(0).getId()); 572 } 573 574 @Test testGetChannelGroups()575 public void testGetChannelGroups() throws Exception { 576 final NotificationChannelGroup ncg = new NotificationChannelGroup("a group", "a label"); 577 ncg.setDescription("bananas"); 578 final NotificationChannelGroup ncg2 = new NotificationChannelGroup("group 2", "label 2"); 579 final NotificationChannel channel = 580 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 581 channel.setGroup(ncg2.getId()); 582 583 mNotificationManager.createNotificationChannelGroup(ncg); 584 mNotificationManager.createNotificationChannelGroup(ncg2); 585 mNotificationManager.createNotificationChannel(channel); 586 587 List<NotificationChannelGroup> actual = 588 mNotificationManager.getNotificationChannelGroups(); 589 assertEquals(2, actual.size()); 590 for (NotificationChannelGroup group : actual) { 591 if (group.getId().equals(ncg.getId())) { 592 assertEquals(group.getName(), ncg.getName().toString()); 593 assertEquals(group.getDescription(), ncg.getDescription()); 594 assertEquals(0, group.getChannels().size()); 595 } else if (group.getId().equals(ncg2.getId())) { 596 assertEquals(group.getName(), ncg2.getName().toString()); 597 assertEquals(group.getDescription(), ncg2.getDescription()); 598 assertEquals(1, group.getChannels().size()); 599 assertEquals(channel.getId(), group.getChannels().get(0).getId()); 600 } else { 601 fail("Extra group found " + group.getId()); 602 } 603 } 604 } 605 606 @Test testDeleteChannelGroup()607 public void testDeleteChannelGroup() throws Exception { 608 final NotificationChannelGroup ncg = new NotificationChannelGroup("a group", "a label"); 609 final NotificationChannel channel = 610 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 611 channel.setGroup(ncg.getId()); 612 mNotificationManager.createNotificationChannelGroup(ncg); 613 mNotificationManager.createNotificationChannel(channel); 614 615 mNotificationManager.deleteNotificationChannelGroup(ncg.getId()); 616 617 assertNull(mNotificationManager.getNotificationChannel(channel.getId())); 618 assertEquals(0, mNotificationManager.getNotificationChannelGroups().size()); 619 } 620 621 @Test testCreateChannel()622 public void testCreateChannel() throws Exception { 623 final NotificationChannel channel = 624 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 625 channel.setDescription("bananas"); 626 channel.enableVibration(true); 627 channel.setVibrationPattern(new long[]{5, 8, 2, 1}); 628 channel.setSound(new Uri.Builder().scheme("test").build(), 629 new AudioAttributes.Builder().setUsage( 630 AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED).build()); 631 channel.enableLights(true); 632 channel.setBypassDnd(true); 633 channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET); 634 mNotificationManager.createNotificationChannel(channel); 635 final NotificationChannel createdChannel = 636 mNotificationManager.getNotificationChannel(mId); 637 compareChannels(channel, createdChannel); 638 // Lockscreen Visibility and canBypassDnd no longer settable. 639 assertTrue(createdChannel.getLockscreenVisibility() != Notification.VISIBILITY_SECRET); 640 assertFalse(createdChannel.canBypassDnd()); 641 } 642 643 @Test testCreateChannel_rename()644 public void testCreateChannel_rename() throws Exception { 645 NotificationChannel channel = 646 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 647 mNotificationManager.createNotificationChannel(channel); 648 channel.setName("new name"); 649 mNotificationManager.createNotificationChannel(channel); 650 final NotificationChannel createdChannel = 651 mNotificationManager.getNotificationChannel(mId); 652 compareChannels(channel, createdChannel); 653 654 channel.setImportance(NotificationManager.IMPORTANCE_HIGH); 655 mNotificationManager.createNotificationChannel(channel); 656 assertEquals(NotificationManager.IMPORTANCE_DEFAULT, 657 mNotificationManager.getNotificationChannel(mId).getImportance()); 658 } 659 660 @Test testCreateChannel_addToGroup()661 public void testCreateChannel_addToGroup() throws Exception { 662 String oldGroup = null; 663 String newGroup = "new group"; 664 mNotificationManager.createNotificationChannelGroup( 665 new NotificationChannelGroup(newGroup, newGroup)); 666 667 NotificationChannel channel = 668 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 669 channel.setGroup(oldGroup); 670 mNotificationManager.createNotificationChannel(channel); 671 672 channel.setGroup(newGroup); 673 mNotificationManager.createNotificationChannel(channel); 674 675 final NotificationChannel updatedChannel = 676 mNotificationManager.getNotificationChannel(mId); 677 assertEquals("Failed to add non-grouped channel to a group on update ", 678 newGroup, updatedChannel.getGroup()); 679 } 680 681 @Test testCreateChannel_cannotChangeGroup()682 public void testCreateChannel_cannotChangeGroup() throws Exception { 683 String oldGroup = "old group"; 684 String newGroup = "new group"; 685 mNotificationManager.createNotificationChannelGroup( 686 new NotificationChannelGroup(oldGroup, oldGroup)); 687 mNotificationManager.createNotificationChannelGroup( 688 new NotificationChannelGroup(newGroup, newGroup)); 689 690 NotificationChannel channel = 691 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 692 channel.setGroup(oldGroup); 693 mNotificationManager.createNotificationChannel(channel); 694 channel.setGroup(newGroup); 695 mNotificationManager.createNotificationChannel(channel); 696 final NotificationChannel updatedChannel = 697 mNotificationManager.getNotificationChannel(mId); 698 assertEquals("Channels should not be allowed to change groups", 699 oldGroup, updatedChannel.getGroup()); 700 } 701 702 @Test testCreateSameChannelDoesNotUpdate()703 public void testCreateSameChannelDoesNotUpdate() throws Exception { 704 final NotificationChannel channel = 705 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 706 mNotificationManager.createNotificationChannel(channel); 707 final NotificationChannel channelDupe = 708 new NotificationChannel(mId, "name", IMPORTANCE_HIGH); 709 mNotificationManager.createNotificationChannel(channelDupe); 710 final NotificationChannel createdChannel = 711 mNotificationManager.getNotificationChannel(mId); 712 compareChannels(channel, createdChannel); 713 } 714 715 @Test testCreateChannelAlreadyExistsNoOp()716 public void testCreateChannelAlreadyExistsNoOp() throws Exception { 717 NotificationChannel channel = 718 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 719 mNotificationManager.createNotificationChannel(channel); 720 NotificationChannel channelDupe = 721 new NotificationChannel(mId, "name", IMPORTANCE_HIGH); 722 mNotificationManager.createNotificationChannel(channelDupe); 723 compareChannels(channel, mNotificationManager.getNotificationChannel(channel.getId())); 724 } 725 726 @Test testCreateChannelWithGroup()727 public void testCreateChannelWithGroup() throws Exception { 728 NotificationChannelGroup ncg = new NotificationChannelGroup("g", "n"); 729 mNotificationManager.createNotificationChannelGroup(ncg); 730 try { 731 NotificationChannel channel = 732 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 733 channel.setGroup(ncg.getId()); 734 mNotificationManager.createNotificationChannel(channel); 735 compareChannels(channel, mNotificationManager.getNotificationChannel(channel.getId())); 736 } finally { 737 mNotificationManager.deleteNotificationChannelGroup(ncg.getId()); 738 } 739 } 740 741 @Test testCreateChannelWithBadGroup()742 public void testCreateChannelWithBadGroup() throws Exception { 743 NotificationChannel channel = 744 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 745 channel.setGroup("garbage"); 746 try { 747 mNotificationManager.createNotificationChannel(channel); 748 fail("Created notification with bad group"); 749 } catch (IllegalArgumentException e) { 750 } 751 } 752 753 @Test testCreateChannelInvalidImportance()754 public void testCreateChannelInvalidImportance() throws Exception { 755 NotificationChannel channel = 756 new NotificationChannel(mId, "name", IMPORTANCE_UNSPECIFIED); 757 try { 758 mNotificationManager.createNotificationChannel(channel); 759 } catch (IllegalArgumentException e) { 760 //success 761 } 762 } 763 764 @Test testDeleteChannel()765 public void testDeleteChannel() throws Exception { 766 NotificationChannel channel = 767 new NotificationChannel(mId, "name", IMPORTANCE_LOW); 768 mNotificationManager.createNotificationChannel(channel); 769 compareChannels(channel, mNotificationManager.getNotificationChannel(channel.getId())); 770 mNotificationManager.deleteNotificationChannel(channel.getId()); 771 assertNull(mNotificationManager.getNotificationChannel(channel.getId())); 772 } 773 774 @Test testCannotDeleteDefaultChannel()775 public void testCannotDeleteDefaultChannel() throws Exception { 776 try { 777 mNotificationManager.deleteNotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID); 778 fail("Deleted default channel"); 779 } catch (IllegalArgumentException e) { 780 //success 781 } 782 } 783 784 @Test testGetChannel()785 public void testGetChannel() throws Exception { 786 NotificationChannel channel1 = 787 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 788 NotificationChannel channel2 = 789 new NotificationChannel( 790 UUID.randomUUID().toString(), "name2", IMPORTANCE_HIGH); 791 NotificationChannel channel3 = 792 new NotificationChannel( 793 UUID.randomUUID().toString(), "name3", IMPORTANCE_LOW); 794 NotificationChannel channel4 = 795 new NotificationChannel( 796 UUID.randomUUID().toString(), "name4", IMPORTANCE_MIN); 797 mNotificationManager.createNotificationChannel(channel1); 798 mNotificationManager.createNotificationChannel(channel2); 799 mNotificationManager.createNotificationChannel(channel3); 800 mNotificationManager.createNotificationChannel(channel4); 801 802 compareChannels(channel2, 803 mNotificationManager.getNotificationChannel(channel2.getId())); 804 compareChannels(channel3, 805 mNotificationManager.getNotificationChannel(channel3.getId())); 806 compareChannels(channel1, 807 mNotificationManager.getNotificationChannel(channel1.getId())); 808 compareChannels(channel4, 809 mNotificationManager.getNotificationChannel(channel4.getId())); 810 } 811 812 @Test testGetChannels()813 public void testGetChannels() throws Exception { 814 NotificationChannel channel1 = 815 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 816 NotificationChannel channel2 = 817 new NotificationChannel( 818 UUID.randomUUID().toString(), "name2", IMPORTANCE_HIGH); 819 NotificationChannel channel3 = 820 new NotificationChannel( 821 UUID.randomUUID().toString(), "name3", IMPORTANCE_LOW); 822 NotificationChannel channel4 = 823 new NotificationChannel( 824 UUID.randomUUID().toString(), "name4", IMPORTANCE_MIN); 825 826 Map<String, NotificationChannel> channelMap = new HashMap<>(); 827 channelMap.put(channel1.getId(), channel1); 828 channelMap.put(channel2.getId(), channel2); 829 channelMap.put(channel3.getId(), channel3); 830 channelMap.put(channel4.getId(), channel4); 831 channelMap.put(NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL); 832 mNotificationManager.createNotificationChannel(channel1); 833 mNotificationManager.createNotificationChannel(channel2); 834 mNotificationManager.createNotificationChannel(channel3); 835 mNotificationManager.createNotificationChannel(channel4); 836 837 mNotificationManager.deleteNotificationChannel(channel3.getId()); 838 839 List<NotificationChannel> channels = mNotificationManager.getNotificationChannels(); 840 for (NotificationChannel nc : channels) { 841 assertFalse(channel3.getId().equals(nc.getId())); 842 if (!channelMap.containsKey(nc.getId())) { 843 fail("Found extra channel " + nc.getId()); 844 } 845 compareChannels(channelMap.get(nc.getId()), nc); 846 } 847 // 1 channel from setUp() (NOTIFICATION_CHANNEL_ID) + 3 randomUUID channels from this test 848 assertEquals(4, channels.size()); 849 } 850 851 @Test testRecreateDeletedChannel()852 public void testRecreateDeletedChannel() throws Exception { 853 NotificationChannel channel = 854 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 855 channel.setShowBadge(true); 856 NotificationChannel newChannel = new NotificationChannel( 857 channel.getId(), channel.getName(), IMPORTANCE_HIGH); 858 mNotificationManager.createNotificationChannel(channel); 859 mNotificationManager.deleteNotificationChannel(channel.getId()); 860 861 mNotificationManager.createNotificationChannel(newChannel); 862 863 compareChannels(channel, 864 mNotificationManager.getNotificationChannel(newChannel.getId())); 865 } 866 867 @Test testNotify()868 public void testNotify() throws Exception { 869 mNotificationManager.cancelAll(); 870 871 final int id = 1; 872 sendNotification(id, R.drawable.black); 873 // test updating the same notification 874 sendNotification(id, R.drawable.blue); 875 sendNotification(id, R.drawable.yellow); 876 877 // assume that sendNotification tested to make sure individual notifications were present 878 StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications(); 879 for (StatusBarNotification sbn : sbns) { 880 if (sbn.getId() != id) { 881 fail("we got back other notifications besides the one we posted: " 882 + sbn.getKey()); 883 } 884 } 885 } 886 887 @Test testNotify_nonexistentChannel_ignored()888 public void testNotify_nonexistentChannel_ignored() { 889 mNotificationManager.cancelAll(); 890 final int id = 404; 891 final Notification notification = 892 new Notification.Builder(mContext, "non_existent_channel") 893 .setSmallIcon(R.drawable.black) 894 .setContentText("This should not be posted!") 895 .build(); 896 897 mNotificationManager.notify(id, notification); 898 899 assertThat(mNotificationHelper.findPostedNotification(null, id, SEARCH_TYPE.APP)).isNull(); 900 assertThat(mNotificationManager.getActiveNotifications()).isEmpty(); 901 } 902 903 @Test testSuspendPackage_withoutShellPermission()904 public void testSuspendPackage_withoutShellPermission() throws Exception { 905 if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) { 906 return; 907 } 908 909 Process proc = Runtime.getRuntime().exec("cmd notification suspend_package " 910 + mContext.getPackageName()); 911 912 // read output of command 913 BufferedReader reader = 914 new BufferedReader(new InputStreamReader(proc.getInputStream())); 915 StringBuilder output = new StringBuilder(); 916 String line = reader.readLine(); 917 while (line != null) { 918 output.append(line); 919 line = reader.readLine(); 920 } 921 reader.close(); 922 final String outputString = output.toString(); 923 924 proc.waitFor(); 925 926 // check that the output string had an error / disallowed call since it didn't have 927 // shell permission to suspend the package 928 assertTrue(outputString, outputString.contains("error")); 929 assertTrue(outputString, outputString.contains("permission denied")); 930 } 931 932 @Test testSuspendPackage()933 public void testSuspendPackage() throws Exception { 934 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 935 assertNotNull(mListener); 936 937 CountDownLatch notificationPostedLatch = mListener.setPostedCountDown(1); 938 sendNotification(1, R.drawable.black); 939 // wait for notification listener to receive notification 940 notificationPostedLatch.await(500, TimeUnit.MILLISECONDS); 941 assertEquals(1, mListener.mPosted.size()); 942 943 CountDownLatch notificationRankingLatch = mListener.setRankingUpdateCountDown(1); 944 // suspend package, ranking should be updated with suspended = true 945 suspendPackage(mContext.getPackageName(), InstrumentationRegistry.getInstrumentation(), 946 true); 947 // wait for notification listener to get response 948 notificationRankingLatch.await(500, TimeUnit.MILLISECONDS); 949 NotificationListenerService.RankingMap rankingMap = mListener.mRankingMap; 950 NotificationListenerService.Ranking outRanking = new NotificationListenerService.Ranking(); 951 for (String key : rankingMap.getOrderedKeys()) { 952 if (key.contains(mListener.getPackageName())) { 953 rankingMap.getRanking(key, outRanking); 954 Log.d(TAG, "key=" + key + " suspended=" + outRanking.isSuspended()); 955 assertTrue(outRanking.isSuspended()); 956 } 957 } 958 959 notificationRankingLatch = mListener.setRankingUpdateCountDown(1); 960 // unsuspend package, ranking should be updated with suspended = false 961 suspendPackage(mContext.getPackageName(), InstrumentationRegistry.getInstrumentation(), 962 false); 963 // wait for notification listener to get response 964 notificationRankingLatch.await(500, TimeUnit.MILLISECONDS); 965 rankingMap = mListener.mRankingMap; 966 for (String key : rankingMap.getOrderedKeys()) { 967 if (key.contains(mListener.getPackageName())) { 968 rankingMap.getRanking(key, outRanking); 969 Log.d(TAG, "key=" + key + " suspended=" + outRanking.isSuspended()); 970 assertFalse(outRanking.isSuspended()); 971 } 972 } 973 974 mListener.resetData(); 975 } 976 977 /** 978 * Binds a service connection to the provided component, then sleeps before returning to ensure 979 * the connection has time to be initialized on low ram devices. 980 */ bindServiceConnection(ComponentName component)981 public FutureServiceConnection bindServiceConnection(ComponentName component) throws Exception { 982 Intent intent = new Intent(); 983 intent.setComponent(component); 984 FutureServiceConnection service_connection = new FutureServiceConnection(); 985 assertTrue(mContext.bindService(intent, service_connection, Context.BIND_AUTO_CREATE)); 986 // We add sleep to give low ram devices a chance to bind; in the case that 987 // bindServiceConnection is called multiple times, attempting too many connections all at 988 // once can increase the time it takes for the service connections to bind, leading to 989 // timeouts. 990 Thread.sleep(4000); 991 return service_connection; 992 } 993 994 /** 995 * Sends a message with the specified notificationId to the provided serviceConnection. 996 */ sendMessage(FutureServiceConnection serviceConnection, int notificationId, Handler callback)997 public void sendMessage(FutureServiceConnection serviceConnection, 998 int notificationId, Handler callback) throws Exception { 999 Messenger service = new Messenger(serviceConnection.get(40000)); 1000 service.send(Message.obtain(null, MESSAGE_SERVICE_NOTIFICATION, notificationId, -1, 1001 new Messenger(callback))); 1002 } 1003 1004 /** 1005 * Sends a cancellation message for the specified notificationId to the serviceConnection. 1006 */ sendCancelAll(FutureServiceConnection serviceConnection, Handler callback)1007 public void sendCancelAll(FutureServiceConnection serviceConnection, 1008 Handler callback) throws Exception { 1009 Messenger service = new Messenger(serviceConnection.get(40000)); 1010 service.send(Message.obtain(null, MESSAGE_CANCEL_ALL_NOTIFICATIONS, -1, -1, 1011 new Messenger(callback))); 1012 } 1013 1014 /** 1015 * Function to identify if the device is a cuttlefish instance. 1016 * The testRankingUpdateSentWithPressure CTS test verifies device behavior that requires large 1017 * numbers of notifications sent in a short period of time, which non-production devices like 1018 * cuttlefish cannot support. 1019 */ onCuttlefish()1020 public static boolean onCuttlefish() throws IOException { 1021 String device = SystemProperties.get("ro.product.device", ""); 1022 String model = SystemProperties.get("ro.product.model", ""); 1023 String name = SystemProperties.get("ro.product.name", ""); 1024 1025 // Return true for cuttlefish instances 1026 if (!device.startsWith("vsoc_")) { 1027 return false; 1028 } 1029 if (!model.startsWith("Cuttlefish ")) { 1030 return false; 1031 } 1032 if (name.startsWith("cf_") || name.startsWith("aosp_cf_")) { 1033 return true; 1034 } 1035 return false; 1036 } 1037 1038 /** 1039 * Tests that, given a significant amount of Notification pressure from 400 notifications 1040 * posted in rapid succession, NotificationListeners don't experience binder errors. 1041 * Timing in this test is tuned to reduce flakiness while avoiding timeouts. Posting a 1042 * notification requires approximately 100 ms, so 500 notifications is expected to take about 1043 * 50 seconds. Tests will time out after approximately 60 seconds, so binding needs to happen 1044 * in under 10 seconds. 1045 */ 1046 @LargeTest 1047 @RequiresDevice 1048 @Test testRankingUpdateSentWithPressure()1049 public void testRankingUpdateSentWithPressure() throws Exception { 1050 // Test should only be run for build in V 1051 if (!SdkLevel.isAtLeastV()) { 1052 return; 1053 } 1054 1055 if (onCuttlefish()) { 1056 return; 1057 } 1058 1059 // TODO(b/307582301): Convert this to assume once we support Junit4 1060 if (!Flags.rankingUpdateAshmem()) { 1061 return; 1062 } 1063 1064 int notificationsPerApp = 40; 1065 int totalNotificationsSent = notificationsPerApp * 8; // 8 apps total 1066 1067 FutureServiceConnection pressureService00 = bindServiceConnection(PRESSURE_SERVICE_00); 1068 FutureServiceConnection pressureService01 = bindServiceConnection(PRESSURE_SERVICE_01); 1069 FutureServiceConnection pressureService02 = bindServiceConnection(PRESSURE_SERVICE_02); 1070 FutureServiceConnection pressureService03 = bindServiceConnection(PRESSURE_SERVICE_03); 1071 FutureServiceConnection pressureService04 = bindServiceConnection(PRESSURE_SERVICE_04); 1072 FutureServiceConnection pressureService05 = bindServiceConnection(PRESSURE_SERVICE_05); 1073 FutureServiceConnection pressureService06 = bindServiceConnection(PRESSURE_SERVICE_06); 1074 FutureServiceConnection pressureService07 = bindServiceConnection(PRESSURE_SERVICE_07); 1075 1076 1077 deactivateGracePeriod(); 1078 setUpNotifListener(); 1079 CountDownLatch notificationPostedLatch = 1080 mListener.setPostedCountDown(totalNotificationsSent); 1081 CountDownLatch notificationRankingUpdateLatch = 1082 mListener.setRankingUpdateCountDown(totalNotificationsSent); 1083 CountDownLatch notificationRemovedLatch = 1084 mListener.setRemovedCountDown(totalNotificationsSent); 1085 1086 mListener.addTestPackage(PRESSURE_APP_00); 1087 mListener.addTestPackage(PRESSURE_APP_01); 1088 mListener.addTestPackage(PRESSURE_APP_02); 1089 mListener.addTestPackage(PRESSURE_APP_03); 1090 mListener.addTestPackage(PRESSURE_APP_04); 1091 mListener.addTestPackage(PRESSURE_APP_05); 1092 mListener.addTestPackage(PRESSURE_APP_06); 1093 mListener.addTestPackage(PRESSURE_APP_07); 1094 1095 // For each app, sends notificationsPerApp notifications each, and ensure that 1096 // none are dropped. 1097 EventCallback callback = new EventCallback(); 1098 int notificationId = 6500; 1099 boolean notificationsReceived = false; 1100 boolean notificationRankingUpdates = false; 1101 boolean notificationsRemoved = false; 1102 for (int i = 0; i < notificationsPerApp; i++) { 1103 sendMessage(pressureService00, notificationId++, callback); 1104 sendMessage(pressureService01, notificationId++, callback); 1105 sendMessage(pressureService02, notificationId++, callback); 1106 sendMessage(pressureService03, notificationId++, callback); 1107 sendMessage(pressureService04, notificationId++, callback); 1108 sendMessage(pressureService05, notificationId++, callback); 1109 sendMessage(pressureService06, notificationId++, callback); 1110 sendMessage(pressureService07, notificationId++, callback); 1111 } 1112 1113 try { 1114 notificationsReceived = notificationPostedLatch.await(50000, TimeUnit.MILLISECONDS); 1115 notificationRankingUpdates = notificationRankingUpdateLatch.await(5000, 1116 TimeUnit.MILLISECONDS); 1117 } catch (InterruptedException e) { 1118 Log.d(TAG, e.toString()); 1119 fail("Interrupted before notifications received or ranking updates received."); 1120 } 1121 1122 // For each app, send a message to cancel all the current notifications. 1123 sendCancelAll(pressureService00, callback); 1124 sendCancelAll(pressureService01, callback); 1125 sendCancelAll(pressureService02, callback); 1126 sendCancelAll(pressureService03, callback); 1127 sendCancelAll(pressureService04, callback); 1128 sendCancelAll(pressureService05, callback); 1129 sendCancelAll(pressureService06, callback); 1130 sendCancelAll(pressureService07, callback); 1131 1132 // Ensure all notifications get removed. 1133 try { 1134 notificationsRemoved = notificationRemovedLatch.await(50000, TimeUnit.MILLISECONDS); 1135 } catch (InterruptedException e) { 1136 Log.d(TAG, e.toString()); 1137 fail("Interrupted before all notifications removed."); 1138 } 1139 1140 assertTrue(notificationsReceived); 1141 assertTrue(notificationRankingUpdates); 1142 assertTrue(notificationsRemoved); 1143 1144 // Clean up. We add sleep to give low ram devices a chance to unbind the service 1145 // successfully without overwhelming the device. 1146 if (pressureService00 != null) { 1147 mContext.unbindService(pressureService00); 1148 pressureService00 = null; 1149 Thread.sleep(900); 1150 } 1151 if (pressureService01 != null) { 1152 mContext.unbindService(pressureService01); 1153 Thread.sleep(900); 1154 } 1155 if (pressureService02 != null) { 1156 mContext.unbindService(pressureService02); 1157 Thread.sleep(900); 1158 } 1159 if (pressureService03 != null) { 1160 mContext.unbindService(pressureService03); 1161 Thread.sleep(900); 1162 } 1163 if (pressureService04 != null) { 1164 mContext.unbindService(pressureService04); 1165 Thread.sleep(900); 1166 } 1167 if (pressureService05 != null) { 1168 mContext.unbindService(pressureService05); 1169 Thread.sleep(900); 1170 } 1171 if (pressureService06 != null) { 1172 mContext.unbindService(pressureService06); 1173 Thread.sleep(900); 1174 } 1175 if (pressureService07 != null) { 1176 mContext.unbindService(pressureService07); 1177 Thread.sleep(900); 1178 } 1179 1180 if (mListener != null) { 1181 mListener.removeTestPackage(PRESSURE_APP_00); 1182 mListener.removeTestPackage(PRESSURE_APP_01); 1183 mListener.removeTestPackage(PRESSURE_APP_02); 1184 mListener.removeTestPackage(PRESSURE_APP_03); 1185 mListener.removeTestPackage(PRESSURE_APP_04); 1186 mListener.removeTestPackage(PRESSURE_APP_05); 1187 mListener.removeTestPackage(PRESSURE_APP_06); 1188 mListener.removeTestPackage(PRESSURE_APP_07); 1189 } 1190 } 1191 1192 @Test testSuspendedPackageSendsNotification()1193 public void testSuspendedPackageSendsNotification() throws Exception { 1194 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 1195 assertNotNull(mListener); 1196 CountDownLatch postedLatch = mListener.setPostedCountDown(1); 1197 1198 // suspend package, post notification while package is suspended, see notification 1199 // in ranking map with suspended = true 1200 suspendPackage(mContext.getPackageName(), InstrumentationRegistry.getInstrumentation(), 1201 true); 1202 sendNotification(1, R.drawable.black); 1203 // wait for notification listener to receive notification 1204 postedLatch.await(500, TimeUnit.MILLISECONDS); 1205 assertEquals(1, mListener.mPosted.size()); // apps targeting P receive notification 1206 NotificationListenerService.RankingMap rankingMap = mListener.mRankingMap; 1207 NotificationListenerService.Ranking outRanking = new NotificationListenerService.Ranking(); 1208 for (String key : rankingMap.getOrderedKeys()) { 1209 if (key.contains(mListener.getPackageName())) { 1210 rankingMap.getRanking(key, outRanking); 1211 Log.d(TAG, "key=" + key + " suspended=" + outRanking.isSuspended()); 1212 assertTrue(outRanking.isSuspended()); 1213 } 1214 } 1215 1216 // unsuspend package, ranking should be updated with suspended = false 1217 suspendPackage(mContext.getPackageName(), InstrumentationRegistry.getInstrumentation(), 1218 false); 1219 Thread.sleep(500); // wait for notification listener to get response 1220 assertEquals(1, mListener.mPosted.size()); // should see previously posted notification 1221 rankingMap = mListener.mRankingMap; 1222 for (String key : rankingMap.getOrderedKeys()) { 1223 if (key.contains(mListener.getPackageName())) { 1224 rankingMap.getRanking(key, outRanking); 1225 Log.d(TAG, "key=" + key + " suspended=" + outRanking.isSuspended()); 1226 assertFalse(outRanking.isSuspended()); 1227 } 1228 } 1229 1230 mListener.resetData(); 1231 } 1232 1233 @Test testShowBadging_ranking()1234 public void testShowBadging_ranking() throws Exception { 1235 final int originalBadging = Settings.Secure.getInt( 1236 mContext.getContentResolver(), Settings.Secure.NOTIFICATION_BADGING); 1237 1238 SystemUtil.runWithShellPermissionIdentity(() -> 1239 Settings.Secure.putInt(mContext.getContentResolver(), 1240 Settings.Secure.NOTIFICATION_BADGING, 1)); 1241 assertEquals(1, Settings.Secure.getInt( 1242 mContext.getContentResolver(), Settings.Secure.NOTIFICATION_BADGING)); 1243 1244 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 1245 assertNotNull(mListener); 1246 CountDownLatch postedLatch = mListener.setPostedCountDown(1); 1247 try { 1248 sendNotification(1, R.drawable.black); 1249 // wait for notification listener to receive notification 1250 postedLatch.await(500, TimeUnit.MILLISECONDS); 1251 NotificationListenerService.RankingMap rankingMap = mListener.mRankingMap; 1252 NotificationListenerService.Ranking outRanking = 1253 new NotificationListenerService.Ranking(); 1254 for (String key : rankingMap.getOrderedKeys()) { 1255 if (key.contains(mListener.getPackageName())) { 1256 rankingMap.getRanking(key, outRanking); 1257 assertTrue(outRanking.canShowBadge()); 1258 } 1259 } 1260 1261 CountDownLatch rankingUpdateLatch = mListener.setRankingUpdateCountDown(1); 1262 1263 // turn off badging globally 1264 SystemUtil.runWithShellPermissionIdentity(() -> 1265 Settings.Secure.putInt(mContext.getContentResolver(), 1266 Settings.Secure.NOTIFICATION_BADGING, 0)); 1267 1268 // wait for ranking update 1269 rankingUpdateLatch.await(500, TimeUnit.MILLISECONDS); 1270 1271 rankingMap = mListener.mRankingMap; 1272 outRanking = new NotificationListenerService.Ranking(); 1273 for (String key : rankingMap.getOrderedKeys()) { 1274 if (key.contains(mListener.getPackageName())) { 1275 assertFalse(outRanking.canShowBadge()); 1276 } 1277 } 1278 1279 mListener.resetData(); 1280 } finally { 1281 SystemUtil.runWithShellPermissionIdentity(() -> 1282 Settings.Secure.putInt(mContext.getContentResolver(), 1283 Settings.Secure.NOTIFICATION_BADGING, originalBadging)); 1284 } 1285 } 1286 1287 @Test testKeyChannelGroupOverrideImportanceExplanation_ranking()1288 public void testKeyChannelGroupOverrideImportanceExplanation_ranking() throws Exception { 1289 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 1290 assertNotNull(mListener); 1291 1292 CountDownLatch postedLatch = mListener.setPostedCountDown(1); 1293 1294 final int notificationId = 1; 1295 sendNotification(notificationId, R.drawable.black); 1296 // wait for notification listener to receive notification 1297 postedLatch.await(500, TimeUnit.MILLISECONDS); 1298 1299 NotificationListenerService.RankingMap rankingMap = mListener.mRankingMap; 1300 NotificationListenerService.Ranking outRanking = 1301 new NotificationListenerService.Ranking(); 1302 1303 StatusBarNotification sbn = mNotificationHelper.findPostedNotification(null, notificationId, 1304 SEARCH_TYPE.POSTED); 1305 1306 // check that the key and channel ids are the same in the ranking as the posted notification 1307 for (String key : rankingMap.getOrderedKeys()) { 1308 if (key.contains(mListener.getPackageName())) { 1309 rankingMap.getRanking(key, outRanking); 1310 1311 // check notification key match 1312 assertEquals(sbn.getKey(), outRanking.getKey()); 1313 1314 // check notification channel ids match 1315 assertEquals(sbn.getNotification().getChannelId(), outRanking.getChannel().getId()); 1316 1317 // check override group key match 1318 assertEquals(sbn.getOverrideGroupKey(), outRanking.getOverrideGroupKey()); 1319 1320 // check importance explanation isn't null 1321 assertNotNull(outRanking.getImportanceExplanation()); 1322 } 1323 } 1324 } 1325 1326 @Test testNotify_blockedChannel()1327 public void testNotify_blockedChannel() throws Exception { 1328 mNotificationManager.cancelAll(); 1329 1330 NotificationChannel channel = 1331 new NotificationChannel(mId, "name", IMPORTANCE_NONE); 1332 mNotificationManager.createNotificationChannel(channel); 1333 1334 int id = 1; 1335 final Notification notification = 1336 new Notification.Builder(mContext, mId) 1337 .setSmallIcon(R.drawable.black) 1338 .setWhen(System.currentTimeMillis()) 1339 .setContentTitle("notify#" + id) 1340 .setContentText("This is #" + id + "notification ") 1341 .build(); 1342 mNotificationManager.notify(id, notification); 1343 1344 assertTrue(mNotificationHelper.isNotificationGone(id, SEARCH_TYPE.APP)); 1345 } 1346 1347 @Test testCancel()1348 public void testCancel() throws Exception { 1349 final int id = 9; 1350 sendNotification(id, R.drawable.black); 1351 // Wait for the notification posted not just enqueued 1352 try { 1353 Thread.sleep(500); 1354 } catch (InterruptedException e) { 1355 } 1356 mNotificationManager.cancel(id); 1357 1358 assertTrue(mNotificationHelper.isNotificationGone(id, SEARCH_TYPE.APP)); 1359 } 1360 1361 @Test testCancelAll()1362 public void testCancelAll() throws Exception { 1363 sendNotification(1, R.drawable.black); 1364 sendNotification(2, R.drawable.blue); 1365 sendNotification(3, R.drawable.yellow); 1366 1367 if (DEBUG) { 1368 Log.d(TAG, "posted 3 notifications, here they are: "); 1369 StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications(); 1370 for (StatusBarNotification sbn : sbns) { 1371 Log.d(TAG, " " + sbn); 1372 } 1373 Log.d(TAG, "about to cancel..."); 1374 } 1375 mNotificationManager.cancelAll(); 1376 1377 for (int id = 1; id <= 3; id++) { 1378 assertTrue(mNotificationHelper.isNotificationGone(id, SEARCH_TYPE.APP)); 1379 } 1380 1381 } 1382 1383 @Test testNotifyWithTimeout()1384 public void testNotifyWithTimeout() throws Exception { 1385 mNotificationManager.cancelAll(); 1386 final int id = 128; 1387 final long timeout = 1000; 1388 1389 final Notification notification = 1390 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 1391 .setSmallIcon(R.drawable.black) 1392 .setContentTitle("notify#" + id) 1393 .setContentText("This is #" + id + "notification ") 1394 .setTimeoutAfter(timeout) 1395 .build(); 1396 mNotificationManager.notify(id, notification); 1397 1398 assertNotNull(mNotificationHelper.findPostedNotification(null, id, SEARCH_TYPE.APP)); 1399 1400 try { 1401 Thread.sleep(timeout); 1402 } catch (InterruptedException ex) { 1403 // pass 1404 } 1405 assertTrue(mNotificationHelper.isNotificationGone(id, SEARCH_TYPE.APP)); 1406 } 1407 1408 @Test testStyle()1409 public void testStyle() throws Exception { 1410 Notification.Style style = new TestStyle(); 1411 1412 Notification.Builder builder = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID); 1413 style.setBuilder(builder); 1414 1415 Notification notification = null; 1416 try { 1417 notification = style.build(); 1418 } catch (IllegalArgumentException e) { 1419 fail(e.getMessage()); 1420 } 1421 1422 assertNotNull(notification); 1423 1424 Notification builderNotification = builder.build(); 1425 assertEquals(builderNotification, notification); 1426 } 1427 1428 @Test testStyle_getStandardView()1429 public void testStyle_getStandardView() throws Exception { 1430 Notification.Builder builder = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID); 1431 int layoutId = 0; 1432 1433 TestStyle overrideStyle = new TestStyle(); 1434 overrideStyle.setBuilder(builder); 1435 RemoteViews result = overrideStyle.testGetStandardView(layoutId); 1436 1437 assertNotNull(result); 1438 assertEquals(layoutId, result.getLayoutId()); 1439 } 1440 1441 private static class TestStyle extends Notification.Style { 1442 @SuppressWarnings("UnusedMethod") areNotificationsVisiblyDifferent(Notification.Style other)1443 public boolean areNotificationsVisiblyDifferent(Notification.Style other) { 1444 return false; 1445 } 1446 testGetStandardView(int layoutId)1447 public RemoteViews testGetStandardView(int layoutId) { 1448 // Wrapper method, since getStandardView is protected and otherwise unused in Android 1449 return getStandardView(layoutId); 1450 } 1451 } 1452 1453 @Test testMediaStyle_empty()1454 public void testMediaStyle_empty() { 1455 Notification.MediaStyle style = new Notification.MediaStyle(); 1456 assertNotNull(style); 1457 } 1458 1459 @Test testMediaStyle()1460 public void testMediaStyle() { 1461 mNotificationManager.cancelAll(); 1462 final int id = 99; 1463 MediaSession session = new MediaSession(mContext, "media"); 1464 1465 final Notification notification = 1466 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 1467 .setSmallIcon(R.drawable.black) 1468 .setContentTitle("notify#" + id) 1469 .setContentText("This is #" + id + "notification ") 1470 .addAction(new Notification.Action.Builder( 1471 Icon.createWithResource(mContext, R.drawable.icon_black), 1472 "play", getPendingIntent()).build()) 1473 .addAction(new Notification.Action.Builder( 1474 Icon.createWithResource(mContext, R.drawable.icon_blue), 1475 "pause", getPendingIntent()).build()) 1476 .setStyle(new Notification.MediaStyle() 1477 .setShowActionsInCompactView(0, 1) 1478 .setMediaSession(session.getSessionToken())) 1479 .build(); 1480 mNotificationManager.notify(id, notification); 1481 1482 assertNotNull(mNotificationHelper.findPostedNotification(null, id, SEARCH_TYPE.APP)); 1483 } 1484 1485 @Test testInboxStyle()1486 public void testInboxStyle() { 1487 final int id = 100; 1488 1489 final Notification notification = 1490 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 1491 .setSmallIcon(R.drawable.black) 1492 .setContentTitle("notify#" + id) 1493 .setContentText("This is #" + id + "notification ") 1494 .addAction(new Notification.Action.Builder( 1495 Icon.createWithResource(mContext, R.drawable.icon_black), 1496 "a1", getPendingIntent()).build()) 1497 .addAction(new Notification.Action.Builder( 1498 Icon.createWithResource(mContext, R.drawable.icon_blue), 1499 "a2", getPendingIntent()).build()) 1500 .setStyle(new Notification.InboxStyle().addLine("line") 1501 .setSummaryText("summary")) 1502 .build(); 1503 mNotificationManager.notify(id, notification); 1504 1505 assertNotNull(mNotificationHelper.findPostedNotification(null, id, SEARCH_TYPE.APP)); 1506 } 1507 1508 @Test testBigTextStyle()1509 public void testBigTextStyle() { 1510 final int id = 101; 1511 1512 final Notification notification = 1513 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 1514 .setSmallIcon(R.drawable.black) 1515 .setContentTitle("notify#" + id) 1516 .setContentText("This is #" + id + "notification ") 1517 .addAction(new Notification.Action.Builder( 1518 Icon.createWithResource(mContext, R.drawable.icon_black), 1519 "a1", getPendingIntent()).build()) 1520 .addAction(new Notification.Action.Builder( 1521 Icon.createWithResource(mContext, R.drawable.icon_blue), 1522 "a2", getPendingIntent()).build()) 1523 .setStyle(new Notification.BigTextStyle() 1524 .setBigContentTitle("big title") 1525 .bigText("big text") 1526 .setSummaryText("summary")) 1527 .build(); 1528 mNotificationManager.notify(id, notification); 1529 1530 assertNotNull(mNotificationHelper.findPostedNotification(null, id, SEARCH_TYPE.APP)); 1531 } 1532 1533 @Test testBigPictureStyle()1534 public void testBigPictureStyle() { 1535 final int id = 102; 1536 1537 final Notification notification = 1538 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 1539 .setSmallIcon(R.drawable.black) 1540 .setContentTitle("notify#" + id) 1541 .setContentText("This is #" + id + "notification ") 1542 .addAction(new Notification.Action.Builder( 1543 Icon.createWithResource(mContext, R.drawable.icon_black), 1544 "a1", getPendingIntent()).build()) 1545 .addAction(new Notification.Action.Builder( 1546 Icon.createWithResource(mContext, R.drawable.icon_blue), 1547 "a2", getPendingIntent()).build()) 1548 .setStyle(new Notification.BigPictureStyle() 1549 .setBigContentTitle("title") 1550 .bigPicture(Bitmap.createBitmap(100, 100, Bitmap.Config.RGB_565)) 1551 .bigLargeIcon( 1552 Icon.createWithResource(mContext, R.drawable.icon_blue)) 1553 .setSummaryText("summary") 1554 .setContentDescription("content description")) 1555 .build(); 1556 mNotificationManager.notify(id, notification); 1557 1558 assertNotNull(mNotificationHelper.findPostedNotification(null, id, SEARCH_TYPE.APP)); 1559 } 1560 1561 @Test testAutogrouping()1562 public void testAutogrouping() throws Exception { 1563 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 1564 assertNotNull(mListener); 1565 CountDownLatch rerankLatch = mListener.setRankingUpdateCountDown(5); 1566 1567 sendNotification(801, R.drawable.black); 1568 sendNotification(802, R.drawable.blue); 1569 sendNotification(803, R.drawable.yellow); 1570 sendNotification(804, R.drawable.yellow); 1571 1572 // Wait until all the notifications, including the autogroup, are posted and grouped. 1573 rerankLatch.await(400, TimeUnit.MILLISECONDS); 1574 assertNotificationCount(5); 1575 assertAllPostedNotificationsAutogrouped(); 1576 } 1577 testAutogrouping_autogroupStaysUntilAllNotificationsCanceled_common( final int numExpectedUpdates)1578 private void testAutogrouping_autogroupStaysUntilAllNotificationsCanceled_common( 1579 final int numExpectedUpdates) throws Exception { 1580 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 1581 assertNotNull(mListener); 1582 CountDownLatch rerankLatch = mListener.setRankingUpdateCountDown(5); 1583 CountDownLatch postingLatch = mListener.setRankingUpdateCountDown(5); 1584 1585 sendNotification(701, R.drawable.black); 1586 sendNotification(702, R.drawable.blue); 1587 sendNotification(703, R.drawable.yellow); 1588 sendNotification(704, R.drawable.yellow); 1589 1590 // Wait until all the notifications, including the autogroup, are posted and grouped. 1591 rerankLatch.await(400, TimeUnit.MILLISECONDS); 1592 postingLatch.await(400, TimeUnit.MILLISECONDS); 1593 assertNotificationCount(5); 1594 assertAllPostedNotificationsAutogrouped(); 1595 1596 // Assert all notis stay in the same autogroup until all children are canceled 1597 CountDownLatch removedLatch; 1598 for (int i = 704; i > 701; i--) { 1599 rerankLatch = mListener.setRankingUpdateCountDown(numExpectedUpdates); 1600 removedLatch = mListener.setRemovedCountDown(numExpectedUpdates); 1601 1602 cancelAndPoll(i); 1603 1604 removedLatch.await(400, TimeUnit.MILLISECONDS); 1605 rerankLatch.await(400, TimeUnit.MILLISECONDS); 1606 assertNotificationCount(i - 700); 1607 assertAllPostedNotificationsAutogrouped(); 1608 } 1609 removedLatch = mListener.setRankingUpdateCountDown(1); 1610 rerankLatch = mListener.setRankingUpdateCountDown(1); 1611 cancelAndPoll(701); 1612 rerankLatch.await(400, TimeUnit.MILLISECONDS); 1613 assertNotificationCount(0); 1614 } 1615 1616 @Test 1617 @RequiresFlagsDisabled(com.android.server.notification.Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE) testAutogrouping_autogroupStaysUntilAllNotificationsCanceled()1618 public void testAutogrouping_autogroupStaysUntilAllNotificationsCanceled() throws Exception { 1619 testAutogrouping_autogroupStaysUntilAllNotificationsCanceled_common(1); 1620 } 1621 1622 @Test 1623 @RequiresFlagsEnabled(com.android.server.notification.Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE) testAutogrouping_autogroupStaysUntilAllNotificationsCanceled_summaryUpdated()1624 public void testAutogrouping_autogroupStaysUntilAllNotificationsCanceled_summaryUpdated() 1625 throws Exception { 1626 testAutogrouping_autogroupStaysUntilAllNotificationsCanceled_common(2); 1627 } 1628 testAutogrouping_autogroupStaysUntilAllNotificationsAddedToGroup_common( final int numExpectedUpdates)1629 private void testAutogrouping_autogroupStaysUntilAllNotificationsAddedToGroup_common( 1630 final int numExpectedUpdates) throws Exception { 1631 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 1632 assertNotNull(mListener); 1633 CountDownLatch rerankLatch = mListener.setRankingUpdateCountDown(5); 1634 CountDownLatch postingLatch = mListener.setPostedCountDown(5); 1635 1636 String newGroup = "new!"; 1637 sendNotification(901, R.drawable.black); 1638 sendNotification(902, R.drawable.blue); 1639 sendNotification(903, R.drawable.yellow); 1640 sendNotification(904, R.drawable.yellow); 1641 1642 List<Integer> postedIds = new ArrayList<>(); 1643 postedIds.add(901); 1644 postedIds.add(902); 1645 postedIds.add(903); 1646 postedIds.add(904); 1647 1648 // Wait until all the notifications, including the autogroup, are posted and grouped. 1649 postingLatch.await(400, TimeUnit.MILLISECONDS); 1650 rerankLatch.await(400, TimeUnit.MILLISECONDS); 1651 assertNotificationCount(5); 1652 assertAllPostedNotificationsAutogrouped(); 1653 1654 // Assert all notis stay in the same autogroup until all children are canceled 1655 for (int i = 904; i > 901; i--) { 1656 postingLatch = mListener.setPostedCountDown(numExpectedUpdates); 1657 rerankLatch = mListener.setRankingUpdateCountDown(numExpectedUpdates); 1658 1659 sendNotification(i, newGroup, R.drawable.blue); 1660 postedIds.remove(postedIds.size() - 1); 1661 1662 postingLatch.await(400, TimeUnit.MILLISECONDS); 1663 rerankLatch.await(400, TimeUnit.MILLISECONDS); 1664 assertNotificationCount(5); 1665 assertOnlySomeNotificationsAutogrouped(postedIds); 1666 } 1667 postingLatch = mListener.setPostedCountDown(1); 1668 rerankLatch = mListener.setRankingUpdateCountDown(1); 1669 sendNotification(901, newGroup, R.drawable.blue); 1670 1671 postingLatch.await(400, TimeUnit.MILLISECONDS); 1672 rerankLatch.await(400, TimeUnit.MILLISECONDS); 1673 assertNotificationCount(4); // no more autogroup summary 1674 postedIds.remove(0); 1675 assertOnlySomeNotificationsAutogrouped(postedIds); 1676 } 1677 1678 @Test 1679 @RequiresFlagsDisabled(com.android.server.notification.Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE) testAutogrouping_autogroupStaysUntilAllNotificationsAddedToGroup()1680 public void testAutogrouping_autogroupStaysUntilAllNotificationsAddedToGroup() 1681 throws Exception { 1682 testAutogrouping_autogroupStaysUntilAllNotificationsAddedToGroup_common(1); 1683 } 1684 1685 @Test 1686 @RequiresFlagsEnabled(com.android.server.notification.Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE) testAutogrouping_autogroupStaysUntilAllNotificationsAddedToGroup_summaryUpdated()1687 public void testAutogrouping_autogroupStaysUntilAllNotificationsAddedToGroup_summaryUpdated() 1688 throws Exception { 1689 testAutogrouping_autogroupStaysUntilAllNotificationsAddedToGroup_common(2); 1690 } 1691 testNewNotificationsAddedToAutogroup_ifOriginalNotificationsCanceled_common( final int numExpectedUpdates)1692 private void testNewNotificationsAddedToAutogroup_ifOriginalNotificationsCanceled_common( 1693 final int numExpectedUpdates) throws Exception { 1694 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 1695 assertNotNull(mListener); 1696 CountDownLatch postingLatch = mListener.setPostedCountDown(5); 1697 CountDownLatch rerankLatch = mListener.setRankingUpdateCountDown(5); 1698 1699 String newGroup = "new!"; 1700 sendNotification(910, R.drawable.black); 1701 sendNotification(920, R.drawable.blue); 1702 sendNotification(930, R.drawable.yellow); 1703 sendNotification(940, R.drawable.yellow); 1704 1705 List<Integer> postedIds = new ArrayList<>(); 1706 postedIds.add(910); 1707 postedIds.add(920); 1708 postedIds.add(930); 1709 postedIds.add(940); 1710 1711 // Wait until all the notifications, including the autogroup, are posted and grouped. 1712 postingLatch.await(400, TimeUnit.MILLISECONDS); 1713 rerankLatch.await(400, TimeUnit.MILLISECONDS); 1714 assertNotificationCount(5); 1715 assertAllPostedNotificationsAutogrouped(); 1716 1717 // regroup all but one of the children 1718 for (int i = postedIds.size() - 1; i > 0; i--) { 1719 postingLatch = mListener.setPostedCountDown(numExpectedUpdates); 1720 rerankLatch = mListener.setRankingUpdateCountDown(numExpectedUpdates); 1721 1722 int id = postedIds.remove(i); 1723 sendNotification(id, newGroup, R.drawable.blue); 1724 1725 postingLatch.await(400, TimeUnit.MILLISECONDS); 1726 rerankLatch.await(400, TimeUnit.MILLISECONDS); 1727 assertNotificationCount(5); 1728 assertOnlySomeNotificationsAutogrouped(postedIds); 1729 } 1730 1731 // send a new non-grouped notification. since the autogroup summary still exists, 1732 // the notification should be added to it 1733 rerankLatch = mListener.setRankingUpdateCountDown(numExpectedUpdates); 1734 postingLatch = mListener.setPostedCountDown(numExpectedUpdates); 1735 sendNotification(950, R.drawable.blue); 1736 postedIds.add(950); 1737 1738 postingLatch.await(400, TimeUnit.MILLISECONDS); 1739 rerankLatch.await(400, TimeUnit.MILLISECONDS); 1740 assertOnlySomeNotificationsAutogrouped(postedIds); 1741 } 1742 1743 @Test 1744 @RequiresFlagsDisabled(com.android.server.notification.Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE) testNewNotificationsAddedToAutogroup_ifOriginalNotificationsCanceled()1745 public void testNewNotificationsAddedToAutogroup_ifOriginalNotificationsCanceled() 1746 throws Exception { 1747 testNewNotificationsAddedToAutogroup_ifOriginalNotificationsCanceled_common(1); 1748 } 1749 1750 @Test 1751 @RequiresFlagsEnabled(com.android.server.notification.Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE) testNewNotificationsAddedToAutogroup_ifOriginalNotificationsCanceled_summaryUpdated()1752 public void testNewNotificationsAddedToAutogroup_ifOriginalNotificationsCanceled_summaryUpdated() 1753 throws Exception { 1754 // The autogroup summary should update as well => wait for 2 notification updates 1755 testNewNotificationsAddedToAutogroup_ifOriginalNotificationsCanceled_common(2); 1756 } 1757 1758 @Test testPostFullScreenIntent_permission()1759 public void testPostFullScreenIntent_permission() { 1760 int id = 6000; 1761 1762 final Notification notification = 1763 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 1764 .setSmallIcon(R.drawable.black) 1765 .setWhen(System.currentTimeMillis()) 1766 .setFullScreenIntent(getPendingIntent(), true) 1767 .setContentText("This is #FSI notification") 1768 .setContentIntent(getPendingIntent()) 1769 .build(); 1770 mNotificationManager.notify(id, notification); 1771 1772 StatusBarNotification n = mNotificationHelper.findPostedNotification( 1773 null, id, SEARCH_TYPE.APP); 1774 assertNotNull(n); 1775 assertEquals(notification.fullScreenIntent, n.getNotification().fullScreenIntent); 1776 } 1777 1778 @Test testNotificationDelegate_grantAndPost()1779 public void testNotificationDelegate_grantAndPost() throws Exception { 1780 final Intent intent = new Intent(mContext, GetResultActivity.class); 1781 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1782 GetResultActivity activity = (GetResultActivity) mInstrumentation.startActivitySync(intent); 1783 mInstrumentation.waitForIdleSync(); 1784 activity.clearResult(); 1785 1786 // grant this test permission to post 1787 final Intent activityIntent = new Intent(); 1788 activityIntent.setPackage(TEST_APP); 1789 activityIntent.setAction(Intent.ACTION_MAIN); 1790 activityIntent.addCategory(Intent.CATEGORY_LAUNCHER); 1791 1792 activity.startActivityForResult(activityIntent, REQUEST_CODE); 1793 assertEquals(RESULT_OK, activity.getResult().resultCode); 1794 1795 // send notification 1796 Notification n = new Notification.Builder(mContext, "channel") 1797 .setSmallIcon(android.R.drawable.ic_media_play) 1798 .build(); 1799 mNotificationManager.notifyAsPackage(TEST_APP, "tag", 0, n); 1800 1801 assertNotNull(mNotificationHelper.findPostedNotification("tag", 0, SEARCH_TYPE.APP)); 1802 final Intent revokeIntent = new Intent(Intent.ACTION_MAIN); 1803 revokeIntent.setClassName(TEST_APP, REVOKE_CLASS); 1804 activity.startActivityForResult(revokeIntent, REQUEST_CODE); 1805 assertEquals(RESULT_OK, activity.getResult().resultCode); 1806 } 1807 1808 @Test testNotificationDelegate_grantAndPostAndCancel()1809 public void testNotificationDelegate_grantAndPostAndCancel() throws Exception { 1810 final Intent intent = new Intent(mContext, GetResultActivity.class); 1811 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1812 GetResultActivity activity = (GetResultActivity) mInstrumentation.startActivitySync(intent); 1813 mInstrumentation.waitForIdleSync(); 1814 activity.clearResult(); 1815 1816 // grant this test permission to post 1817 final Intent activityIntent = new Intent(); 1818 activityIntent.setPackage(TEST_APP); 1819 activityIntent.setAction(Intent.ACTION_MAIN); 1820 activityIntent.addCategory(Intent.CATEGORY_LAUNCHER); 1821 1822 activity.startActivityForResult(activityIntent, REQUEST_CODE); 1823 assertEquals(RESULT_OK, activity.getResult().resultCode); 1824 1825 // send notification 1826 Notification n = new Notification.Builder(mContext, "channel") 1827 .setSmallIcon(android.R.drawable.ic_media_play) 1828 .build(); 1829 mNotificationManager.notifyAsPackage(TEST_APP, "toBeCanceled", 10000, n); 1830 assertNotNull(mNotificationHelper.findPostedNotification("toBeCanceled", 10000, 1831 SEARCH_TYPE.APP)); 1832 mNotificationManager.cancelAsPackage(TEST_APP, "toBeCanceled", 10000); 1833 assertTrue(mNotificationHelper.isNotificationGone(10000, SEARCH_TYPE.APP)); 1834 final Intent revokeIntent = new Intent(Intent.ACTION_MAIN); 1835 revokeIntent.setClassName(TEST_APP, REVOKE_CLASS); 1836 activity.startActivityForResult(revokeIntent, REQUEST_CODE); 1837 assertEquals(RESULT_OK, activity.getResult().resultCode); 1838 } 1839 1840 @Test testNotificationDelegate_cannotCancelNotificationsPostedByDelegator()1841 public void testNotificationDelegate_cannotCancelNotificationsPostedByDelegator() 1842 throws Exception { 1843 final Intent intent = new Intent(mContext, GetResultActivity.class); 1844 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1845 GetResultActivity activity = (GetResultActivity) mInstrumentation.startActivitySync(intent); 1846 mInstrumentation.waitForIdleSync(); 1847 activity.clearResult(); 1848 1849 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 1850 assertNotNull(mListener); 1851 1852 // grant this test permission to post 1853 final Intent activityIntent = new Intent(Intent.ACTION_MAIN); 1854 activityIntent.setClassName(TEST_APP, DELEGATE_POST_CLASS); 1855 1856 activity.startActivityForResult(activityIntent, REQUEST_CODE); 1857 assertEquals(RESULT_OK, activity.getResult().resultCode); 1858 1859 assertNotNull(mNotificationHelper.findPostedNotification(null, 9, SEARCH_TYPE.LISTENER)); 1860 1861 try { 1862 mNotificationManager.cancelAsPackage(TEST_APP, null, 9); 1863 fail("Delegate should not be able to cancel notification they did not post"); 1864 } catch (SecurityException e) { 1865 // yay 1866 } 1867 1868 // double check that the notification does still exist 1869 assertNotNull(mNotificationHelper.findPostedNotification(null, 9, SEARCH_TYPE.LISTENER)); 1870 1871 final Intent revokeIntent = new Intent(Intent.ACTION_MAIN); 1872 revokeIntent.setClassName(TEST_APP, REVOKE_CLASS); 1873 activity.startActivityForResult(revokeIntent, REQUEST_CODE); 1874 assertEquals(RESULT_OK, activity.getResult().resultCode); 1875 } 1876 1877 @Test testNotificationDelegate_grantAndReadChannels()1878 public void testNotificationDelegate_grantAndReadChannels() throws Exception { 1879 final Intent intent = new Intent(mContext, GetResultActivity.class); 1880 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1881 GetResultActivity activity = (GetResultActivity) mInstrumentation.startActivitySync(intent); 1882 mInstrumentation.waitForIdleSync(); 1883 activity.clearResult(); 1884 1885 // grant this test permission to post 1886 final Intent activityIntent = new Intent(); 1887 activityIntent.setPackage(TEST_APP); 1888 activityIntent.setAction(Intent.ACTION_MAIN); 1889 activityIntent.addCategory(Intent.CATEGORY_LAUNCHER); 1890 1891 activity.startActivityForResult(activityIntent, REQUEST_CODE); 1892 assertEquals(RESULT_OK, activity.getResult().resultCode); 1893 1894 List<NotificationChannel> channels = 1895 mContext.createPackageContextAsUser(TEST_APP, /* flags= */ 0, mContext.getUser()) 1896 .getSystemService(NotificationManager.class) 1897 .getNotificationChannels(); 1898 1899 assertNotNull(channels); 1900 1901 final Intent revokeIntent = new Intent(Intent.ACTION_MAIN); 1902 revokeIntent.setClassName(TEST_APP, REVOKE_CLASS); 1903 activity.startActivityForResult(revokeIntent, REQUEST_CODE); 1904 assertEquals(RESULT_OK, activity.getResult().resultCode); 1905 } 1906 1907 @Test testNotificationDelegate_grantAndReadChannel()1908 public void testNotificationDelegate_grantAndReadChannel() throws Exception { 1909 final Intent intent = new Intent(mContext, GetResultActivity.class); 1910 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1911 GetResultActivity activity = (GetResultActivity) mInstrumentation.startActivitySync(intent); 1912 mInstrumentation.waitForIdleSync(); 1913 activity.clearResult(); 1914 1915 // grant this test permission to post 1916 final Intent activityIntent = new Intent(); 1917 activityIntent.setPackage(TEST_APP); 1918 activityIntent.setAction(Intent.ACTION_MAIN); 1919 activityIntent.addCategory(Intent.CATEGORY_LAUNCHER); 1920 1921 activity.startActivityForResult(activityIntent, REQUEST_CODE); 1922 assertEquals(RESULT_OK, activity.getResult().resultCode); 1923 1924 NotificationChannel channel = 1925 mContext.createPackageContextAsUser(TEST_APP, /* flags= */ 0, mContext.getUser()) 1926 .getSystemService(NotificationManager.class) 1927 .getNotificationChannel("channel"); 1928 1929 assertNotNull(channel); 1930 1931 final Intent revokeIntent = new Intent(Intent.ACTION_MAIN); 1932 revokeIntent.setClassName(TEST_APP, REVOKE_CLASS); 1933 activity.startActivityForResult(revokeIntent, REQUEST_CODE); 1934 assertEquals(RESULT_OK, activity.getResult().resultCode); 1935 } 1936 1937 @Test testNotificationDelegate_grantAndRevoke()1938 public void testNotificationDelegate_grantAndRevoke() throws Exception { 1939 final Intent intent = new Intent(mContext, GetResultActivity.class); 1940 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1941 GetResultActivity activity = (GetResultActivity) mInstrumentation.startActivitySync(intent); 1942 mInstrumentation.waitForIdleSync(); 1943 activity.clearResult(); 1944 1945 // grant this test permission to post 1946 final Intent activityIntent = new Intent(); 1947 activityIntent.setPackage(TEST_APP); 1948 activityIntent.setAction(Intent.ACTION_MAIN); 1949 activityIntent.addCategory(Intent.CATEGORY_LAUNCHER); 1950 1951 activity.startActivityForResult(activityIntent, REQUEST_CODE); 1952 assertEquals(RESULT_OK, activity.getResult().resultCode); 1953 1954 assertTrue(mNotificationManager.canNotifyAsPackage(TEST_APP)); 1955 1956 final Intent revokeIntent = new Intent(Intent.ACTION_MAIN); 1957 revokeIntent.setClassName(TEST_APP, REVOKE_CLASS); 1958 activity.startActivityForResult(revokeIntent, REQUEST_CODE); 1959 assertEquals(RESULT_OK, activity.getResult().resultCode); 1960 1961 try { 1962 // send notification 1963 Notification n = new Notification.Builder(mContext, "channel") 1964 .setSmallIcon(android.R.drawable.ic_media_play) 1965 .build(); 1966 mNotificationManager.notifyAsPackage(TEST_APP, "tag", 0, n); 1967 fail("Should not be able to post as a delegate when permission revoked"); 1968 } catch (SecurityException e) { 1969 // yay 1970 } 1971 } 1972 1973 @Test testNotificationIcon()1974 public void testNotificationIcon() throws Exception { 1975 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 1976 assertNotNull(mListener); 1977 1978 CountDownLatch postedLatch = mListener.setPostedCountDown(2); 1979 1980 int id = 6000; 1981 1982 Notification notification = 1983 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 1984 .setSmallIcon(android.R.drawable.ic_media_play) 1985 .setWhen(System.currentTimeMillis()) 1986 .setFullScreenIntent(getPendingIntent(), true) 1987 .setContentText("This notification has a resource icon") 1988 .setContentIntent(getPendingIntent()) 1989 .build(); 1990 mNotificationManager.notify(id, notification); 1991 1992 notification = 1993 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 1994 .setSmallIcon(Icon.createWithResource(mContext, R.drawable.icon_black)) 1995 .setWhen(System.currentTimeMillis()) 1996 .setFullScreenIntent(getPendingIntent(), true) 1997 .setContentText("This notification has an Icon icon") 1998 .setContentIntent(getPendingIntent()) 1999 .build(); 2000 mNotificationManager.notify(id, notification); 2001 postedLatch.await(POST_TIMEOUT, TimeUnit.MILLISECONDS); 2002 2003 StatusBarNotification n = mNotificationHelper.findPostedNotification( 2004 null, id, SEARCH_TYPE.POSTED); 2005 assertNotNull(n); 2006 } 2007 2008 @Test testShouldHideSilentStatusIcons()2009 public void testShouldHideSilentStatusIcons() throws Exception { 2010 try { 2011 mNotificationManager.shouldHideSilentStatusBarIcons(); 2012 fail("Non-privileged apps should not get this information"); 2013 } catch (SecurityException e) { 2014 // pass 2015 } 2016 2017 mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 2018 // no exception this time 2019 mNotificationManager.shouldHideSilentStatusBarIcons(); 2020 } 2021 2022 /* Confirm that the optional methods of TestNotificationListener still exist and 2023 * don't fail. */ 2024 @Test testNotificationListenerMethods()2025 public void testNotificationListenerMethods() { 2026 NotificationListenerService listener = new TestNotificationListener(); 2027 listener.onListenerConnected(); 2028 2029 listener.onSilentStatusBarIconsVisibilityChanged(false); 2030 2031 listener.onNotificationPosted(null); 2032 listener.onNotificationPosted(null, null); 2033 2034 listener.onNotificationRemoved(null); 2035 listener.onNotificationRemoved(null, null); 2036 2037 listener.onNotificationChannelGroupModified("", UserHandle.CURRENT, null, 2038 NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_ADDED); 2039 listener.onNotificationChannelModified("", UserHandle.CURRENT, null, 2040 NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_ADDED); 2041 2042 listener.onListenerDisconnected(); 2043 } 2044 performNotificationProviderAction(@onNull String action)2045 private void performNotificationProviderAction(@NonNull String action) { 2046 // Create an intent to launch an activity which just posts or cancels notifications 2047 Intent activityIntent = new Intent(Intent.ACTION_MAIN); 2048 activityIntent.setClassName(NOTIFICATIONPROVIDER, RICH_NOTIFICATION_ACTIVITY); 2049 activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 2050 activityIntent.putExtra("action", action); 2051 mContext.startActivity(activityIntent); 2052 } 2053 2054 @Test testNotificationUriPermissionsGranted()2055 public void testNotificationUriPermissionsGranted() throws Exception { 2056 Uri background7Uri = Uri.parse( 2057 "content://com.android.test.notificationprovider.provider/background7.png"); 2058 Uri background8Uri = Uri.parse( 2059 "content://com.android.test.notificationprovider.provider/background8.png"); 2060 2061 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 2062 assertNotNull(mListener); 2063 2064 CountDownLatch postedLatch = mListener.setPostedCountDown(1); 2065 2066 try { 2067 // Post #7 2068 performNotificationProviderAction("send-7"); 2069 2070 postedLatch.await(POST_TIMEOUT, TimeUnit.MILLISECONDS); 2071 assertEquals(background7Uri, getNotificationBackgroundImageUri(7)); 2072 assertTrue(mNotificationHelper.isNotificationGone(8, SEARCH_TYPE.LISTENER)); 2073 assertAccessible(background7Uri); 2074 assertInaccessible(background8Uri); 2075 2076 // Reset the notification posted latch. 2077 postedLatch = mListener.setPostedCountDown(1); 2078 2079 // Post #8 2080 performNotificationProviderAction("send-8"); 2081 2082 postedLatch.await(POST_TIMEOUT, TimeUnit.MILLISECONDS); 2083 assertEquals(background7Uri, getNotificationBackgroundImageUri(7)); 2084 assertEquals(background8Uri, getNotificationBackgroundImageUri(8)); 2085 assertAccessible(background7Uri); 2086 assertAccessible(background8Uri); 2087 2088 // Add a notification removed latch. 2089 CountDownLatch removedLatch = mListener.setRemovedCountDown(1); 2090 2091 // Cancel #7 2092 performNotificationProviderAction("cancel-7"); 2093 2094 removedLatch.await(POST_TIMEOUT, TimeUnit.MILLISECONDS); 2095 assertTrue(mNotificationHelper.isNotificationGone(7, SEARCH_TYPE.LISTENER)); 2096 assertEquals(background8Uri, getNotificationBackgroundImageUri(8)); 2097 assertInaccessible(background7Uri); 2098 assertAccessible(background8Uri); 2099 2100 // Reset the notification reemoved latch. 2101 removedLatch = mListener.setRemovedCountDown(1); 2102 2103 // Cancel #8 2104 performNotificationProviderAction("cancel-8"); 2105 2106 removedLatch.await(POST_TIMEOUT, TimeUnit.MILLISECONDS); 2107 assertTrue(mNotificationHelper.isNotificationGone(7, SEARCH_TYPE.LISTENER)); 2108 assertTrue(mNotificationHelper.isNotificationGone(8, SEARCH_TYPE.LISTENER)); 2109 assertInaccessible(background7Uri); 2110 assertInaccessible(background8Uri); 2111 2112 } finally { 2113 // Clean up -- reset any remaining notifications 2114 performNotificationProviderAction("reset"); 2115 Thread.sleep(500); 2116 } 2117 } 2118 2119 @Test testNotificationUriPermissionsGrantedToNewListeners()2120 public void testNotificationUriPermissionsGrantedToNewListeners() throws Exception { 2121 Uri background7Uri = Uri.parse( 2122 "content://com.android.test.notificationprovider.provider/background7.png"); 2123 2124 try { 2125 // Post #7 2126 performNotificationProviderAction("send-7"); 2127 // Don't have access the notification yet, but we can test the URI 2128 assertInaccessible(background7Uri); 2129 2130 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 2131 assertNotNull(mListener); 2132 2133 mNotificationHelper.findPostedNotification(null, 7, SEARCH_TYPE.LISTENER); 2134 2135 assertEquals(background7Uri, getNotificationBackgroundImageUri(7)); 2136 assertAccessible(background7Uri); 2137 2138 } finally { 2139 // Clean Up -- Cancel #7 2140 performNotificationProviderAction("cancel-7"); 2141 Thread.sleep(500); 2142 } 2143 } 2144 2145 @Test testNotificationUriPermissionsRevokedFromRemovedListeners()2146 public void testNotificationUriPermissionsRevokedFromRemovedListeners() throws Exception { 2147 Uri background7Uri = Uri.parse( 2148 "content://com.android.test.notificationprovider.provider/background7.png"); 2149 2150 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 2151 mListener = TestNotificationListener.getInstance(); 2152 assertNotNull(mListener); 2153 2154 try { 2155 // Post #7 2156 performNotificationProviderAction("send-7"); 2157 mNotificationHelper.findPostedNotification(null, 7, SEARCH_TYPE.POSTED); 2158 2159 assertEquals(background7Uri, getNotificationBackgroundImageUri(7)); 2160 assertAccessible(background7Uri); 2161 2162 // Remove the listener to ensure permissions get revoked 2163 mNotificationHelper.disableListener(STUB_PACKAGE_NAME); 2164 Thread.sleep(500); // wait for listener to be disabled 2165 2166 assertInaccessible(background7Uri); 2167 2168 } finally { 2169 // Clean Up -- Cancel #7 2170 performNotificationProviderAction("cancel-7"); 2171 Thread.sleep(500); 2172 } 2173 } 2174 2175 private class NotificationListenerConnection implements ServiceConnection { 2176 private final Semaphore mSemaphore = new Semaphore(0); 2177 2178 @Override onServiceConnected(ComponentName className, IBinder service)2179 public void onServiceConnected(ComponentName className, IBinder service) { 2180 if (URI_ACCESS_SERVICE.equals(className)) { 2181 mNotificationUriAccessService = INotificationUriAccessService.Stub.asInterface( 2182 service); 2183 } 2184 if (NLS_CONTROL_SERVICE.equals(className)) { 2185 mNLSControlService = INLSControlService.Stub.asInterface(service); 2186 } 2187 mSemaphore.release(); 2188 } 2189 2190 @Override onServiceDisconnected(ComponentName className)2191 public void onServiceDisconnected(ComponentName className) { 2192 if (URI_ACCESS_SERVICE.equals(className)) { 2193 mNotificationUriAccessService = null; 2194 } 2195 if (NLS_CONTROL_SERVICE.equals(className)) { 2196 mNLSControlService = null; 2197 } 2198 } 2199 waitForService()2200 public void waitForService() { 2201 try { 2202 if (mSemaphore.tryAcquire(5, TimeUnit.SECONDS)) { 2203 return; 2204 } 2205 } catch (InterruptedException e) { 2206 } 2207 fail("failed to connec to service"); 2208 } 2209 } 2210 2211 @Test testNotificationUriPermissionsRevokedOnlyFromRemovedListeners()2212 public void testNotificationUriPermissionsRevokedOnlyFromRemovedListeners() throws Exception { 2213 Uri background7Uri = Uri.parse( 2214 "content://com.android.test.notificationprovider.provider/background7.png"); 2215 2216 // Connect to a service in the NotificationListener app which allows us to validate URI 2217 // permissions granted to a second app, so that we show that permissions aren't being 2218 // revoked too broadly. 2219 final Intent intent = new Intent(); 2220 intent.setComponent(URI_ACCESS_SERVICE); 2221 NotificationListenerConnection connection = new NotificationListenerConnection(); 2222 mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE); 2223 connection.waitForService(); 2224 2225 // Before starting the test, make sure the service works, that there is no listener, and 2226 // that the URI starts inaccessible to that process. 2227 mNotificationUriAccessService.ensureNotificationListenerServiceConnected(false); 2228 assertFalse(mNotificationUriAccessService.isFileUriAccessible(background7Uri)); 2229 2230 // Give the NotificationListener app access to notifications, and validate that. 2231 toggleExternalListenerAccess(new ComponentName("com.android.test.notificationlistener", 2232 "com.android.test.notificationlistener.TestNotificationListener"), true); 2233 Thread.sleep(500); 2234 mNotificationUriAccessService.ensureNotificationListenerServiceConnected(true); 2235 assertFalse(mNotificationUriAccessService.isFileUriAccessible(background7Uri)); 2236 2237 // Give the test app access to notifications, and get that listener 2238 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 2239 assertNotNull(mListener); 2240 2241 try { 2242 try { 2243 // Post #7 2244 performNotificationProviderAction("send-7"); 2245 2246 // Check that both the test app (this code) and the external app have URI access. 2247 assertEquals(background7Uri, getNotificationBackgroundImageUri(7)); 2248 assertAccessible(background7Uri); 2249 assertTrue(mNotificationUriAccessService.isFileUriAccessible(background7Uri)); 2250 2251 // Remove the external listener to ensure permissions get revoked 2252 toggleExternalListenerAccess( 2253 new ComponentName("com.android.test.notificationlistener", 2254 "com.android.test.notificationlistener.TestNotificationListener"), 2255 false); 2256 Thread.sleep(500); // wait for listener to be disabled 2257 2258 // Ensure that revoking listener access to this one app does not affect the other: 2259 // external app no longer has access, this one still does 2260 assertFalse(mNotificationUriAccessService.isFileUriAccessible(background7Uri)); 2261 assertAccessible(background7Uri); 2262 2263 } finally { 2264 // Clean Up -- Cancel #7 2265 performNotificationProviderAction("cancel-7"); 2266 Thread.sleep(500); 2267 } 2268 2269 // Finally, cancelling the notification must still revoke those other permissions. 2270 // Double-check first that the notification is actually gone, and then wait for a bit 2271 // longer, as it may take some time for the uri permissions to clear up even after the 2272 // notification is gone. 2273 assertTrue(mNotificationHelper.isNotificationGone(7, SEARCH_TYPE.LISTENER)); 2274 Thread.sleep(500); 2275 assertInaccessible(background7Uri); 2276 } finally { 2277 // Clean Up -- Make sure this app has access revoked 2278 mNotificationHelper.disableListener(STUB_PACKAGE_NAME); 2279 } 2280 } 2281 2282 @Test testNotificationListenerRequestUnbind()2283 public void testNotificationListenerRequestUnbind() throws Exception { 2284 final Intent intent = new Intent(); 2285 intent.setComponent(NLS_CONTROL_SERVICE); 2286 NotificationListenerConnection connection = new NotificationListenerConnection(); 2287 mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE); 2288 connection.waitForService(); 2289 2290 // Give the NotificationListener app access to notifications, and validate that. 2291 toggleExternalListenerAccess(NO_AUTOBIND_NLS, true); 2292 Thread.sleep(500); 2293 2294 // Give the test app access to notifications, and get that listener 2295 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 2296 assertNotNull(mListener); 2297 2298 try { 2299 // Check that the listener service is not auto-bound (manifest meta-data) 2300 assertFalse(mNLSControlService.isNotificationListenerConnected()); 2301 2302 // Request bind NLS 2303 mNLSControlService.requestRebindComponent(); 2304 Thread.sleep(500); 2305 assertTrue(mNLSControlService.isNotificationListenerConnected()); 2306 2307 // Request unbind NLS 2308 mNLSControlService.requestUnbindComponent(); 2309 Thread.sleep(500); 2310 assertFalse(mNLSControlService.isNotificationListenerConnected()); 2311 } finally { 2312 // Clean Up -- Make sure the external listener is has access revoked 2313 toggleExternalListenerAccess(NO_AUTOBIND_NLS, false); 2314 } 2315 } 2316 2317 @Test testNotificationListenerAutobindMetaData()2318 public void testNotificationListenerAutobindMetaData() throws Exception { 2319 final ServiceInfo info = mPackageManager.getServiceInfo(NO_AUTOBIND_NLS, 2320 PackageManager.GET_META_DATA 2321 | PackageManager.MATCH_DIRECT_BOOT_AWARE 2322 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE); 2323 2324 assertNotNull(info); 2325 assertTrue(info.metaData.containsKey(META_DATA_DEFAULT_AUTOBIND)); 2326 assertFalse(info.metaData.getBoolean(META_DATA_DEFAULT_AUTOBIND, true)); 2327 } 2328 assertAccessible(Uri uri)2329 private void assertAccessible(Uri uri) 2330 throws IOException { 2331 ContentResolver contentResolver = mContext.getContentResolver(); 2332 for (int tries = 5; tries-- > 0; ) { 2333 try (AssetFileDescriptor fd = contentResolver.openAssetFile(uri, "r", null)) { 2334 if (fd != null) { 2335 return; 2336 } 2337 } catch (SecurityException e) { 2338 } 2339 try { 2340 Thread.sleep(200); 2341 } catch (InterruptedException ex) { 2342 } 2343 } 2344 fail("Uri " + uri + "is not accessible"); 2345 } 2346 assertInaccessible(Uri uri)2347 private void assertInaccessible(Uri uri) 2348 throws IOException { 2349 ContentResolver contentResolver = mContext.getContentResolver(); 2350 for (int tries = 5; tries-- > 0; ) { 2351 try (AssetFileDescriptor fd = contentResolver.openAssetFile(uri, "r", null)) { 2352 } catch (SecurityException e) { 2353 return; 2354 } 2355 try { 2356 Thread.sleep(200); 2357 } catch (InterruptedException ex) { 2358 } 2359 } 2360 fail("Uri " + uri + "is still accessible"); 2361 } 2362 2363 @NonNull getNotificationBackgroundImageUri(int notificationId)2364 private Uri getNotificationBackgroundImageUri(int notificationId) { 2365 StatusBarNotification sbn = mNotificationHelper.findPostedNotification(null, 2366 notificationId, SEARCH_TYPE.LISTENER); 2367 assertNotNull(sbn); 2368 String imageUriString = sbn.getNotification().extras 2369 .getString(Notification.EXTRA_BACKGROUND_IMAGE_URI); 2370 assertNotNull(imageUriString); 2371 return Uri.parse(imageUriString); 2372 } 2373 uncheck(ThrowingSupplier<T> supplier)2374 private <T> T uncheck(ThrowingSupplier<T> supplier) { 2375 try { 2376 return supplier.get(); 2377 } catch (Exception e) { 2378 throw new CompletionException(e); 2379 } 2380 } 2381 2382 @Test testNotificationListener_setNotificationsShown()2383 public void testNotificationListener_setNotificationsShown() throws Exception { 2384 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 2385 assertNotNull(mListener); 2386 CountDownLatch postedLatch = mListener.setPostedCountDown(2); 2387 final int notificationId1 = 1003; 2388 final int notificationId2 = 1004; 2389 2390 sendNotification(notificationId1, R.drawable.black); 2391 sendNotification(notificationId2, R.drawable.black); 2392 // wait for notification listener to receive notification 2393 postedLatch.await(500, TimeUnit.MILLISECONDS); 2394 2395 StatusBarNotification sbn1 = mNotificationHelper.findPostedNotification( 2396 null, notificationId1, SEARCH_TYPE.LISTENER); 2397 StatusBarNotification sbn2 = mNotificationHelper.findPostedNotification( 2398 null, notificationId2, SEARCH_TYPE.LISTENER); 2399 mListener.setNotificationsShown(new String[]{sbn1.getKey()}); 2400 2401 mNotificationHelper.disableListener(STUB_PACKAGE_NAME); 2402 Thread.sleep(500); // wait for listener to be disallowed 2403 try { 2404 mListener.setNotificationsShown(new String[]{sbn2.getKey()}); 2405 fail("Should not be able to set shown if listener access isn't granted"); 2406 } catch (SecurityException e) { 2407 // expected 2408 } 2409 } 2410 2411 @Test testNotificationListener_getNotificationChannels()2412 public void testNotificationListener_getNotificationChannels() throws Exception { 2413 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 2414 assertNotNull(mListener); 2415 2416 try { 2417 mListener.getNotificationChannels(mContext.getPackageName(), UserHandle.CURRENT); 2418 fail("Shouldn't be able get channels without CompanionDeviceManager#getAssociations()"); 2419 } catch (SecurityException e) { 2420 // expected 2421 } 2422 } 2423 2424 @Test testNotificationListener_getNotificationChannelGroups()2425 public void testNotificationListener_getNotificationChannelGroups() throws Exception { 2426 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 2427 assertNotNull(mListener); 2428 try { 2429 mListener.getNotificationChannelGroups(mContext.getPackageName(), UserHandle.CURRENT); 2430 fail("Should not be able get groups without CompanionDeviceManager#getAssociations()"); 2431 } catch (SecurityException e) { 2432 // expected 2433 } 2434 } 2435 2436 @Test testNotificationListener_updateNotificationChannel()2437 public void testNotificationListener_updateNotificationChannel() throws Exception { 2438 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 2439 assertNotNull(mListener); 2440 2441 NotificationChannel channel = new NotificationChannel( 2442 NOTIFICATION_CHANNEL_ID, "name", IMPORTANCE_DEFAULT); 2443 try { 2444 mListener.updateNotificationChannel(mContext.getPackageName(), UserHandle.CURRENT, 2445 channel); 2446 fail("Shouldn't be able to update channel without " 2447 + "CompanionDeviceManager#getAssociations()"); 2448 } catch (SecurityException e) { 2449 // expected 2450 } 2451 } 2452 2453 @Test testNotificationListener_getActiveNotifications()2454 public void testNotificationListener_getActiveNotifications() throws Exception { 2455 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 2456 assertNotNull(mListener); 2457 CountDownLatch postedLatch = mListener.setPostedCountDown(2); 2458 final int notificationId1 = 1001; 2459 final int notificationId2 = 1002; 2460 2461 sendNotification(notificationId1, R.drawable.black); 2462 sendNotification(notificationId2, R.drawable.black); 2463 // wait for notification listener to receive notification 2464 postedLatch.await(500, TimeUnit.MILLISECONDS); 2465 2466 StatusBarNotification sbn1 = mNotificationHelper.findPostedNotification( 2467 null, notificationId1, SEARCH_TYPE.LISTENER); 2468 StatusBarNotification sbn2 = mNotificationHelper.findPostedNotification( 2469 null, notificationId2, SEARCH_TYPE.LISTENER); 2470 StatusBarNotification[] notifs = 2471 mListener.getActiveNotifications(new String[]{sbn2.getKey(), sbn1.getKey()}); 2472 assertEquals(sbn2.getKey(), notifs[0].getKey()); 2473 assertEquals(sbn2.getId(), notifs[0].getId()); 2474 assertEquals(sbn2.getPackageName(), notifs[0].getPackageName()); 2475 2476 assertEquals(sbn1.getKey(), notifs[1].getKey()); 2477 assertEquals(sbn1.getId(), notifs[1].getId()); 2478 assertEquals(sbn1.getPackageName(), notifs[1].getPackageName()); 2479 } 2480 2481 2482 @Test testNotificationListener_getCurrentRanking()2483 public void testNotificationListener_getCurrentRanking() throws Exception { 2484 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 2485 assertNotNull(mListener); 2486 CountDownLatch rankingUpdateLatch = mListener.setRankingUpdateCountDown(1); 2487 2488 sendNotification(1, R.drawable.black); 2489 rankingUpdateLatch.await(POST_TIMEOUT, TimeUnit.MILLISECONDS); 2490 mNotificationHelper.findPostedNotification(null, 1, SEARCH_TYPE.POSTED); 2491 2492 assertEquals(mListener.mRankingMap, mListener.getCurrentRanking()); 2493 } 2494 2495 @Test testNotificationListener_cancelNotifications()2496 public void testNotificationListener_cancelNotifications() throws Exception { 2497 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 2498 assertNotNull(mListener); 2499 final int notificationId = 1006; 2500 2501 CountDownLatch postedLatch = mListener.setPostedCountDown(1); 2502 sendNotification(notificationId, R.drawable.black); 2503 // wait for notification listener to receive notification 2504 postedLatch.await(POST_TIMEOUT, TimeUnit.MILLISECONDS); 2505 2506 StatusBarNotification sbn = mNotificationHelper.findPostedNotification(null, notificationId, 2507 SEARCH_TYPE.LISTENER); 2508 2509 mListener.cancelNotification(sbn.getPackageName(), sbn.getTag(), sbn.getId()); 2510 // Beginning with Lollipop, this cancelNotification signature no longer cancels the 2511 // notification. 2512 if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) { 2513 assertNotNull(mNotificationHelper.findPostedNotification(null, notificationId, 2514 SEARCH_TYPE.LISTENER)); 2515 } else { 2516 // Tested in LegacyNotificationManager20Test 2517 assertTrue(mNotificationHelper.isNotificationGone( 2518 notificationId, SEARCH_TYPE.LISTENER)); 2519 } 2520 2521 mListener.cancelNotifications(new String[]{sbn.getKey()}); 2522 if (getCancellationReason(sbn.getKey()) 2523 != NotificationListenerService.REASON_LISTENER_CANCEL) { 2524 fail("Failed to cancel notification id=" + notificationId); 2525 } 2526 } 2527 2528 @Test testNotificationAssistant_cancelNotifications()2529 public void testNotificationAssistant_cancelNotifications() throws Exception { 2530 mAssistant = mNotificationHelper.enableAssistant(STUB_PACKAGE_NAME); 2531 assertNotNull(mAssistant); 2532 final int notificationId = 1006; 2533 2534 sendNotification(notificationId, R.drawable.black); 2535 Thread.sleep(500); // wait for notification listener to receive notification 2536 2537 StatusBarNotification sbn = mNotificationHelper.findPostedNotification(null, notificationId, 2538 SEARCH_TYPE.APP); 2539 2540 mAssistant.cancelNotifications(new String[]{sbn.getKey()}); 2541 int gotReason = getAssistantCancellationReason(sbn.getKey()); 2542 if (gotReason != NotificationListenerService.REASON_ASSISTANT_CANCEL) { 2543 fail("Failed cancellation from assistant, notification id=" + notificationId 2544 + "; got reason=" + gotReason); 2545 } 2546 } 2547 2548 @Test testNotificationManagerPolicy_priorityCategoriesToString()2549 public void testNotificationManagerPolicy_priorityCategoriesToString() { 2550 String zeroString = NotificationManager.Policy.priorityCategoriesToString(0); 2551 assertEquals("priorityCategories of 0 produces empty string", "", zeroString); 2552 2553 String oneString = NotificationManager.Policy.priorityCategoriesToString(1); 2554 assertNotNull("priorityCategories of 1 returns a string", oneString); 2555 boolean lengthGreaterThanZero = oneString.length() > 0; 2556 assertTrue("priorityCategories of 1 returns a string with length greater than 0", 2557 lengthGreaterThanZero); 2558 2559 String badNumberString = NotificationManager.Policy.priorityCategoriesToString(1234567); 2560 assertNotNull("priorityCategories with a non-relevant int returns a string", 2561 badNumberString); 2562 } 2563 2564 @Test testNotificationManagerPolicy_prioritySendersToString()2565 public void testNotificationManagerPolicy_prioritySendersToString() { 2566 String zeroString = NotificationManager.Policy.prioritySendersToString(0); 2567 assertNotNull("prioritySenders of 1 returns a string", zeroString); 2568 boolean lengthGreaterThanZero = zeroString.length() > 0; 2569 assertTrue("prioritySenders of 1 returns a string with length greater than 0", 2570 lengthGreaterThanZero); 2571 2572 String badNumberString = NotificationManager.Policy.prioritySendersToString(1234567); 2573 assertNotNull("prioritySenders with a non-relevant int returns a string", badNumberString); 2574 } 2575 2576 @Test testNotificationManagerPolicy_suppressedEffectsToString()2577 public void testNotificationManagerPolicy_suppressedEffectsToString() { 2578 String zeroString = NotificationManager.Policy.suppressedEffectsToString(0); 2579 assertEquals("suppressedEffects of 0 produces empty string", "", zeroString); 2580 2581 String oneString = NotificationManager.Policy.suppressedEffectsToString(1); 2582 assertNotNull("suppressedEffects of 1 returns a string", oneString); 2583 boolean lengthGreaterThanZero = oneString.length() > 0; 2584 assertTrue("suppressedEffects of 1 returns a string with length greater than 0", 2585 lengthGreaterThanZero); 2586 2587 String badNumberString = NotificationManager.Policy.suppressedEffectsToString(1234567); 2588 assertNotNull("suppressedEffects with a non-relevant int returns a string", 2589 badNumberString); 2590 } 2591 2592 @Test testOriginalChannelImportance()2593 public void testOriginalChannelImportance() { 2594 NotificationChannel channel = new NotificationChannel(mId, "my channel", IMPORTANCE_HIGH); 2595 2596 mNotificationManager.createNotificationChannel(channel); 2597 2598 NotificationChannel actual = mNotificationManager.getNotificationChannel(channel.getId()); 2599 assertEquals(IMPORTANCE_HIGH, actual.getImportance()); 2600 assertEquals(IMPORTANCE_HIGH, actual.getOriginalImportance()); 2601 2602 // Apps are allowed to downgrade channel importance if the user has not changed any 2603 // fields on this channel yet. 2604 channel.setImportance(IMPORTANCE_DEFAULT); 2605 mNotificationManager.createNotificationChannel(channel); 2606 2607 actual = mNotificationManager.getNotificationChannel(channel.getId()); 2608 assertEquals(IMPORTANCE_DEFAULT, actual.getImportance()); 2609 assertEquals(IMPORTANCE_HIGH, actual.getOriginalImportance()); 2610 } 2611 2612 @Test testCreateConversationChannel()2613 public void testCreateConversationChannel() { 2614 final NotificationChannel channel = 2615 new NotificationChannel(mId, "Messages", IMPORTANCE_DEFAULT); 2616 2617 String conversationId = "person a"; 2618 2619 final NotificationChannel conversationChannel = 2620 new NotificationChannel(mId + "child", 2621 "Messages from " + conversationId, IMPORTANCE_DEFAULT); 2622 conversationChannel.setConversationId(channel.getId(), conversationId); 2623 2624 mNotificationManager.createNotificationChannel(channel); 2625 mNotificationManager.createNotificationChannel(conversationChannel); 2626 2627 compareChannels(conversationChannel, 2628 mNotificationManager.getNotificationChannel(channel.getId(), conversationId)); 2629 } 2630 2631 @Test testConversationRankingFields()2632 public void testConversationRankingFields() throws Exception { 2633 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 2634 assertNotNull(mListener); 2635 CountDownLatch postedLatch = mListener.setPostedCountDown(1); 2636 2637 createDynamicShortcut(); 2638 mNotificationManager.notify(177, getConversationNotification().build()); 2639 2640 // wait for notification listener to receive notification 2641 postedLatch.await(500, TimeUnit.MILLISECONDS); 2642 assertNotNull(mNotificationHelper.findPostedNotification(null, 177, SEARCH_TYPE.LISTENER)); 2643 assertEquals(1, mListener.mPosted.size()); 2644 2645 NotificationListenerService.RankingMap rankingMap = mListener.mRankingMap; 2646 NotificationListenerService.Ranking outRanking = new NotificationListenerService.Ranking(); 2647 for (String key : rankingMap.getOrderedKeys()) { 2648 if (key.contains(mListener.getPackageName())) { 2649 rankingMap.getRanking(key, outRanking); 2650 assertTrue(outRanking.isConversation()); 2651 assertEquals(SHARE_SHORTCUT_ID, outRanking.getConversationShortcutInfo().getId()); 2652 } 2653 } 2654 } 2655 2656 @Test testDemoteConversationChannel()2657 public void testDemoteConversationChannel() { 2658 final NotificationChannel channel = 2659 new NotificationChannel(mId, "Messages", IMPORTANCE_DEFAULT); 2660 2661 String conversationId = "person a"; 2662 2663 final NotificationChannel conversationChannel = 2664 new NotificationChannel(mId + "child", 2665 "Messages from " + conversationId, IMPORTANCE_DEFAULT); 2666 conversationChannel.setConversationId(channel.getId(), conversationId); 2667 2668 mNotificationManager.createNotificationChannel(channel); 2669 mNotificationManager.createNotificationChannel(conversationChannel); 2670 2671 conversationChannel.setDemoted(true); 2672 2673 SystemUtil.runWithShellPermissionIdentity(() -> 2674 mNotificationManager.updateNotificationChannel( 2675 mContext.getPackageName(), android.os.Process.myUid(), channel)); 2676 2677 assertEquals(false, mNotificationManager.getNotificationChannel( 2678 channel.getId(), conversationId).isDemoted()); 2679 } 2680 2681 @Test testDeleteConversationChannels()2682 public void testDeleteConversationChannels() throws Exception { 2683 setUpNotifListener(); 2684 CountDownLatch postedLatch = mListener.setPostedCountDown(1); 2685 2686 createDynamicShortcut(); 2687 2688 final NotificationChannel channel = 2689 new NotificationChannel(mId, "Messages", IMPORTANCE_DEFAULT); 2690 2691 final NotificationChannel conversationChannel = 2692 new NotificationChannel(mId + "child", 2693 "Messages from " + SHARE_SHORTCUT_ID, IMPORTANCE_DEFAULT); 2694 conversationChannel.setConversationId(channel.getId(), SHARE_SHORTCUT_ID); 2695 2696 mNotificationManager.createNotificationChannel(channel); 2697 mNotificationManager.createNotificationChannel(conversationChannel); 2698 2699 mNotificationManager.notify(177, getConversationNotification().build()); 2700 2701 // wait for notification listener to receive notification 2702 postedLatch.await(500, TimeUnit.MILLISECONDS); 2703 assertNotNull(mNotificationHelper.findPostedNotification(null, 177, SEARCH_TYPE.LISTENER)); 2704 assertEquals(1, mListener.mPosted.size()); 2705 2706 deleteShortcuts(); 2707 2708 Thread.sleep(300); // wait for deletion to propagate 2709 2710 assertFalse(mNotificationManager.getNotificationChannel(channel.getId(), 2711 conversationChannel.getConversationId()).isConversation()); 2712 2713 } 2714 2715 /** 2716 * This method verifies that an app can't bypass background restrictions by retrieving their own 2717 * notification and triggering it. 2718 */ 2719 @AsbSecurityTest(cveBugId = 185388103) 2720 @Test testActivityStartFromRetrievedNotification_isBlocked()2721 public void testActivityStartFromRetrievedNotification_isBlocked() throws Exception { 2722 deactivateGracePeriod(); 2723 EventCallback callback = new EventCallback(); 2724 int notificationId = 6007; 2725 2726 // Post notification and fire its pending intent 2727 sendTrampolineMessage(TRAMPOLINE_SERVICE_API_30, MESSAGE_SERVICE_NOTIFICATION, 2728 notificationId, callback); 2729 PollingCheck.waitFor(TIMEOUT_MS, () -> uncheck(() -> { 2730 sendTrampolineMessage(TRAMPOLINE_SERVICE, MESSAGE_CLICK_NOTIFICATION, notificationId, 2731 callback); 2732 // timeoutMs = 1ms below because surrounding waitFor already handles retry & timeout. 2733 return callback.waitFor(EventCallback.NOTIFICATION_CLICKED, /* timeoutMs */ 1); 2734 })); 2735 2736 assertFalse("Activity start should have been blocked", 2737 callback.waitFor(EventCallback.ACTIVITY_STARTED, TIMEOUT_MS)); 2738 } 2739 2740 @Test testActivityStartOnBroadcastTrampoline_isBlocked()2741 public void testActivityStartOnBroadcastTrampoline_isBlocked() throws Exception { 2742 deactivateGracePeriod(); 2743 setUpNotifListener(); 2744 mListener.addTestPackage(TRAMPOLINE_APP); 2745 CountDownLatch postedLatch = mListener.setPostedCountDown(1); 2746 EventCallback callback = new EventCallback(); 2747 int notificationId = 6001; 2748 2749 // Post notification and fire its pending intent 2750 sendTrampolineMessage(TRAMPOLINE_SERVICE, MESSAGE_BROADCAST_NOTIFICATION, notificationId, 2751 callback); 2752 postedLatch.await(POST_TIMEOUT, TimeUnit.MILLISECONDS); 2753 StatusBarNotification statusBarNotification = mNotificationHelper.findPostedNotification( 2754 null, notificationId, SEARCH_TYPE.LISTENER); 2755 assertNotNull("Notification not posted on time", statusBarNotification); 2756 statusBarNotification.getNotification().contentIntent.send(); 2757 2758 assertTrue("Broadcast not received on time", 2759 callback.waitFor(EventCallback.BROADCAST_RECEIVED, TIMEOUT_LONG_MS)); 2760 assertFalse("Activity start should have been blocked", 2761 callback.waitFor(EventCallback.ACTIVITY_STARTED, TIMEOUT_MS)); 2762 } 2763 2764 @Test testActivityStartOnServiceTrampoline_isBlocked()2765 public void testActivityStartOnServiceTrampoline_isBlocked() throws Exception { 2766 deactivateGracePeriod(); 2767 setUpNotifListener(); 2768 mListener.addTestPackage(TRAMPOLINE_APP); 2769 CountDownLatch postedLatch = mListener.setPostedCountDown(1); 2770 EventCallback callback = new EventCallback(); 2771 int notificationId = 6002; 2772 2773 // Post notification and fire its pending intent 2774 sendTrampolineMessage(TRAMPOLINE_SERVICE, MESSAGE_SERVICE_NOTIFICATION, notificationId, 2775 callback); 2776 postedLatch.await(POST_TIMEOUT, TimeUnit.MILLISECONDS); 2777 StatusBarNotification statusBarNotification = mNotificationHelper.findPostedNotification( 2778 null, notificationId, SEARCH_TYPE.LISTENER); 2779 assertNotNull("Notification not posted on time", statusBarNotification); 2780 statusBarNotification.getNotification().contentIntent.send(); 2781 2782 assertTrue("Service not started on time", 2783 callback.waitFor(EventCallback.SERVICE_STARTED, TIMEOUT_MS)); 2784 assertFalse("Activity start should have been blocked", 2785 callback.waitFor(EventCallback.ACTIVITY_STARTED, TIMEOUT_MS)); 2786 } 2787 2788 @Test testActivityStartOnBroadcastTrampoline_whenApi30_isAllowed()2789 public void testActivityStartOnBroadcastTrampoline_whenApi30_isAllowed() throws Exception { 2790 deactivateGracePeriod(); 2791 setUpNotifListener(); 2792 mListener.addTestPackage(TRAMPOLINE_APP_API_30); 2793 CountDownLatch postedLatch = mListener.setPostedCountDown(1); 2794 EventCallback callback = new EventCallback(); 2795 int notificationId = 6003; 2796 2797 // Post notification and fire its pending intent 2798 sendTrampolineMessage(TRAMPOLINE_SERVICE_API_30, MESSAGE_BROADCAST_NOTIFICATION, 2799 notificationId, callback); 2800 postedLatch.await(POST_TIMEOUT, TimeUnit.MILLISECONDS); 2801 StatusBarNotification statusBarNotification = mNotificationHelper.findPostedNotification( 2802 null, notificationId, SEARCH_TYPE.LISTENER); 2803 assertNotNull("Notification not posted on time", statusBarNotification); 2804 statusBarNotification.getNotification().contentIntent.send(); 2805 2806 assertTrue("Broadcast not received on time", 2807 callback.waitFor(EventCallback.BROADCAST_RECEIVED, TIMEOUT_LONG_MS)); 2808 assertTrue("Activity not started", 2809 callback.waitFor(EventCallback.ACTIVITY_STARTED, TIMEOUT_MS)); 2810 } 2811 2812 @Test testActivityStartOnServiceTrampoline_whenApi30_isAllowed()2813 public void testActivityStartOnServiceTrampoline_whenApi30_isAllowed() throws Exception { 2814 deactivateGracePeriod(); 2815 setUpNotifListener(); 2816 mListener.addTestPackage(TRAMPOLINE_APP_API_30); 2817 CountDownLatch postedLatch = mListener.setPostedCountDown(1); 2818 EventCallback callback = new EventCallback(); 2819 int notificationId = 6004; 2820 2821 // Post notification and fire its pending intent 2822 sendTrampolineMessage(TRAMPOLINE_SERVICE_API_30, MESSAGE_SERVICE_NOTIFICATION, 2823 notificationId, callback); 2824 postedLatch.await(POST_TIMEOUT, TimeUnit.MILLISECONDS); 2825 StatusBarNotification statusBarNotification = mNotificationHelper.findPostedNotification( 2826 null, notificationId, SEARCH_TYPE.LISTENER); 2827 assertNotNull("Notification not posted on time", statusBarNotification); 2828 statusBarNotification.getNotification().contentIntent.send(); 2829 2830 assertTrue("Service not started on time", 2831 callback.waitFor(EventCallback.SERVICE_STARTED, TIMEOUT_MS)); 2832 assertTrue("Activity not started", 2833 callback.waitFor(EventCallback.ACTIVITY_STARTED, TIMEOUT_MS)); 2834 } 2835 2836 @Test testActivityStartOnBroadcastTrampoline_whenDefaultBrowser_isBlocked()2837 public void testActivityStartOnBroadcastTrampoline_whenDefaultBrowser_isBlocked() 2838 throws Exception { 2839 deactivateGracePeriod(); 2840 setDefaultBrowser(TRAMPOLINE_APP); 2841 setUpNotifListener(); 2842 mListener.addTestPackage(TRAMPOLINE_APP); 2843 CountDownLatch postedLatch = mListener.setPostedCountDown(1); 2844 EventCallback callback = new EventCallback(); 2845 int notificationId = 6005; 2846 2847 // Post notification and fire its pending intent 2848 sendTrampolineMessage(TRAMPOLINE_SERVICE, MESSAGE_BROADCAST_NOTIFICATION, notificationId, 2849 callback); 2850 postedLatch.await(POST_TIMEOUT, TimeUnit.MILLISECONDS); 2851 StatusBarNotification statusBarNotification = mNotificationHelper.findPostedNotification( 2852 null, notificationId, SEARCH_TYPE.LISTENER); 2853 assertNotNull("Notification not posted on time", statusBarNotification); 2854 statusBarNotification.getNotification().contentIntent.send(); 2855 2856 assertTrue("Broadcast not received on time", 2857 callback.waitFor(EventCallback.BROADCAST_RECEIVED, TIMEOUT_LONG_MS)); 2858 assertFalse("Activity started", 2859 callback.waitFor(EventCallback.ACTIVITY_STARTED, TIMEOUT_MS)); 2860 } 2861 2862 @Test testActivityStartOnBroadcastTrampoline_whenDefaultBrowserApi32_isAllowed()2863 public void testActivityStartOnBroadcastTrampoline_whenDefaultBrowserApi32_isAllowed() 2864 throws Exception { 2865 deactivateGracePeriod(); 2866 setDefaultBrowser(TRAMPOLINE_APP_API_32); 2867 setUpNotifListener(); 2868 mListener.addTestPackage(TRAMPOLINE_APP_API_32); 2869 CountDownLatch postedLatch = mListener.setPostedCountDown(1); 2870 EventCallback callback = new EventCallback(); 2871 int notificationId = 6005; 2872 2873 // Post notification and fire its pending intent 2874 sendTrampolineMessage(TRAMPOLINE_SERVICE_API_32, MESSAGE_BROADCAST_NOTIFICATION, 2875 notificationId, callback); 2876 postedLatch.await(POST_TIMEOUT, TimeUnit.MILLISECONDS); 2877 StatusBarNotification statusBarNotification = mNotificationHelper.findPostedNotification( 2878 null, notificationId, SEARCH_TYPE.LISTENER); 2879 assertNotNull("Notification not posted on time", statusBarNotification); 2880 statusBarNotification.getNotification().contentIntent.send(); 2881 2882 assertTrue("Broadcast not received on time", 2883 callback.waitFor(EventCallback.BROADCAST_RECEIVED, TIMEOUT_LONG_MS)); 2884 assertTrue("Activity not started", 2885 callback.waitFor(EventCallback.ACTIVITY_STARTED, TIMEOUT_MS)); 2886 } 2887 2888 @Test testActivityStartOnServiceTrampoline_whenDefaultBrowser_isBlocked()2889 public void testActivityStartOnServiceTrampoline_whenDefaultBrowser_isBlocked() 2890 throws Exception { 2891 deactivateGracePeriod(); 2892 setDefaultBrowser(TRAMPOLINE_APP); 2893 setUpNotifListener(); 2894 mListener.addTestPackage(TRAMPOLINE_APP); 2895 CountDownLatch postedLatch = mListener.setPostedCountDown(1); 2896 EventCallback callback = new EventCallback(); 2897 int notificationId = 6006; 2898 2899 // Post notification and fire its pending intent 2900 sendTrampolineMessage(TRAMPOLINE_SERVICE, MESSAGE_SERVICE_NOTIFICATION, notificationId, 2901 callback); 2902 postedLatch.await(POST_TIMEOUT, TimeUnit.MILLISECONDS); 2903 StatusBarNotification statusBarNotification = mNotificationHelper.findPostedNotification( 2904 null, notificationId, SEARCH_TYPE.LISTENER); 2905 assertNotNull("Notification not posted on time", statusBarNotification); 2906 statusBarNotification.getNotification().contentIntent.send(); 2907 2908 assertTrue("Service not started on time", 2909 callback.waitFor(EventCallback.SERVICE_STARTED, TIMEOUT_MS)); 2910 assertFalse("Activity started", 2911 callback.waitFor(EventCallback.ACTIVITY_STARTED, TIMEOUT_MS)); 2912 } 2913 2914 @Test testActivityStartOnServiceTrampoline_whenDefaultBrowserApi32_isAllowed()2915 public void testActivityStartOnServiceTrampoline_whenDefaultBrowserApi32_isAllowed() 2916 throws Exception { 2917 deactivateGracePeriod(); 2918 setDefaultBrowser(TRAMPOLINE_APP_API_32); 2919 setUpNotifListener(); 2920 mListener.addTestPackage(TRAMPOLINE_APP_API_32); 2921 CountDownLatch postedLatch = mListener.setPostedCountDown(1); 2922 EventCallback callback = new EventCallback(); 2923 int notificationId = 6006; 2924 2925 // Post notification and fire its pending intent 2926 sendTrampolineMessage(TRAMPOLINE_SERVICE_API_32, MESSAGE_SERVICE_NOTIFICATION, 2927 notificationId, callback); 2928 postedLatch.await(POST_TIMEOUT, TimeUnit.MILLISECONDS); 2929 StatusBarNotification statusBarNotification = mNotificationHelper.findPostedNotification( 2930 null, notificationId, SEARCH_TYPE.LISTENER); 2931 assertNotNull("Notification not posted on time", statusBarNotification); 2932 statusBarNotification.getNotification().contentIntent.send(); 2933 2934 assertTrue("Service not started on time", 2935 callback.waitFor(EventCallback.SERVICE_STARTED, TIMEOUT_MS)); 2936 assertTrue("Activity not started", 2937 callback.waitFor(EventCallback.ACTIVITY_STARTED, TIMEOUT_MS)); 2938 } 2939 2940 @Test testGrantRevokeNotificationManagerApis_works()2941 public void testGrantRevokeNotificationManagerApis_works() { 2942 SystemUtil.runWithShellPermissionIdentity(() -> { 2943 ComponentName componentName = 2944 new ComponentName(STUB_PACKAGE_NAME, TestNotificationListener.class.getName()); 2945 mNotificationManager.setNotificationListenerAccessGranted( 2946 componentName, true, true); 2947 2948 assertThat( 2949 mNotificationManager.getEnabledNotificationListeners(), 2950 hasItem(componentName)); 2951 2952 mNotificationManager.setNotificationListenerAccessGranted( 2953 componentName, false, false); 2954 2955 assertThat( 2956 "Non-user-set changes should not override user-set", 2957 mNotificationManager.getEnabledNotificationListeners(), 2958 hasItem(componentName)); 2959 }); 2960 } 2961 2962 @Test testGrantRevokeNotificationManagerApis_exclusiveToPermissionController()2963 public void testGrantRevokeNotificationManagerApis_exclusiveToPermissionController() { 2964 List<PackageInfo> allPackages = mPackageManager.getInstalledPackages( 2965 PackageManager.MATCH_DISABLED_COMPONENTS 2966 | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS); 2967 List<String> allowedPackages = Arrays.asList( 2968 mPackageManager.getPermissionControllerPackageName(), 2969 "com.android.shell"); 2970 StringBuilder sb = new StringBuilder(); 2971 for (PackageInfo pkg : allPackages) { 2972 if (!pkg.applicationInfo.isSystemApp() 2973 && mPackageManager.checkPermission( 2974 Manifest.permission.MANAGE_NOTIFICATION_LISTENERS, pkg.packageName) 2975 == PackageManager.PERMISSION_GRANTED 2976 && !allowedPackages.contains(pkg.packageName)) { 2977 sb.append(pkg.packageName + " can't hold " 2978 + Manifest.permission.MANAGE_NOTIFICATION_LISTENERS + "\n"); 2979 } 2980 } 2981 if (sb.length() > 0) { 2982 fail(sb.toString()); 2983 } 2984 } 2985 2986 @Test testChannelDeletion_cancelReason()2987 public void testChannelDeletion_cancelReason() throws Exception { 2988 setUpNotifListener(); 2989 CountDownLatch notificationPostedLatch = mListener.setPostedCountDown(1); 2990 2991 sendNotification(566, R.drawable.black); 2992 // wait for notification listener to receive notification 2993 notificationPostedLatch.await(500, TimeUnit.MILLISECONDS); 2994 assertEquals(1, mListener.mPosted.size()); 2995 String key = mListener.mPosted.get(0).getKey(); 2996 2997 mNotificationManager.deleteNotificationChannel(NOTIFICATION_CHANNEL_ID); 2998 2999 assertEquals(NotificationListenerService.REASON_CHANNEL_REMOVED, 3000 getCancellationReason(key)); 3001 } 3002 3003 @Test testMediaStyle_setNoClearFlag()3004 public void testMediaStyle_setNoClearFlag() { 3005 // Test should only be run for build in V 3006 if (!SdkLevel.isAtLeastV()) { 3007 return; 3008 } 3009 if (!CompatChanges.isChangeEnabled(ENFORCE_NO_CLEAR_FLAG_ON_MEDIA_NOTIFICATION)) { 3010 Log.d(TAG, "Skipping testMediaStyle_setNoClearFlag(), SDK_INT=" 3011 + Build.VERSION.SDK_INT); 3012 return; 3013 } 3014 int id = 99; 3015 final Notification notification = 3016 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 3017 .setSmallIcon(R.drawable.black) 3018 .setStyle(new Notification.MediaStyle()) 3019 .build(); 3020 mNotificationManager.notify(id, notification); 3021 3022 StatusBarNotification sbn = mNotificationHelper.findPostedNotification(null, id, 3023 SEARCH_TYPE.APP); 3024 assertNotNull(sbn); 3025 3026 assertEquals(FLAG_NO_CLEAR, sbn.getNotification().flags & FLAG_NO_CLEAR); 3027 } 3028 3029 @Test testCustomMediaStyle_setNoClearFlag()3030 public void testCustomMediaStyle_setNoClearFlag() { 3031 // Test should only be run for build in V 3032 if (!SdkLevel.isAtLeastV()) { 3033 return; 3034 } 3035 if (!CompatChanges.isChangeEnabled(ENFORCE_NO_CLEAR_FLAG_ON_MEDIA_NOTIFICATION)) { 3036 Log.d(TAG, "Skipping testCustomMediaStyle_setNoClearFlag(), SDK_INT=" 3037 + Build.VERSION.SDK_INT); 3038 return; 3039 } 3040 int id = 99; 3041 final Notification notification = 3042 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 3043 .setSmallIcon(R.drawable.black) 3044 .setStyle(new Notification.DecoratedMediaCustomViewStyle()) 3045 .build(); 3046 mNotificationManager.notify(id, notification); 3047 3048 StatusBarNotification sbn = mNotificationHelper.findPostedNotification(null, id, 3049 SEARCH_TYPE.APP); 3050 assertNotNull(sbn); 3051 3052 assertEquals(FLAG_NO_CLEAR, sbn.getNotification().flags & FLAG_NO_CLEAR); 3053 } 3054 3055 @Test testMediaStyleRemotePlayback_noPermission()3056 public void testMediaStyleRemotePlayback_noPermission() throws Exception { 3057 int id = 99; 3058 final String deviceName = "device name"; 3059 final int deviceIcon = 123; 3060 final PendingIntent deviceIntent = getPendingIntent(); 3061 final Notification notification = 3062 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 3063 .setSmallIcon(R.drawable.black) 3064 .setStyle(new Notification.MediaStyle() 3065 .setRemotePlaybackInfo(deviceName, deviceIcon, deviceIntent)) 3066 .build(); 3067 mNotificationManager.notify(id, notification); 3068 3069 StatusBarNotification sbn = mNotificationHelper.findPostedNotification(null, id, 3070 SEARCH_TYPE.APP); 3071 assertNotNull(sbn); 3072 3073 assertFalse(sbn.getNotification().extras 3074 .containsKey(Notification.EXTRA_MEDIA_REMOTE_DEVICE)); 3075 assertFalse(sbn.getNotification().extras 3076 .containsKey(Notification.EXTRA_MEDIA_REMOTE_ICON)); 3077 assertFalse(sbn.getNotification().extras 3078 .containsKey(Notification.EXTRA_MEDIA_REMOTE_INTENT)); 3079 } 3080 3081 @Test testMediaStyleRemotePlayback_hasPermission()3082 public void testMediaStyleRemotePlayback_hasPermission() throws Exception { 3083 int id = 99; 3084 final String deviceName = "device name"; 3085 final int deviceIcon = 123; 3086 final PendingIntent deviceIntent = getPendingIntent(); 3087 final Notification notification = 3088 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 3089 .setSmallIcon(R.drawable.black) 3090 .setStyle(new Notification.MediaStyle() 3091 .setRemotePlaybackInfo(deviceName, deviceIcon, deviceIntent)) 3092 .build(); 3093 3094 SystemUtil.runWithShellPermissionIdentity(() -> { 3095 mNotificationManager.notify(id, notification); 3096 }, android.Manifest.permission.MEDIA_CONTENT_CONTROL); 3097 3098 StatusBarNotification sbn = mNotificationHelper.findPostedNotification( 3099 null, id, SEARCH_TYPE.APP); 3100 assertNotNull(sbn); 3101 assertEquals(deviceName, sbn.getNotification().extras 3102 .getString(Notification.EXTRA_MEDIA_REMOTE_DEVICE)); 3103 assertEquals(deviceIcon, sbn.getNotification().extras 3104 .getInt(Notification.EXTRA_MEDIA_REMOTE_ICON)); 3105 assertEquals(deviceIntent, sbn.getNotification().extras 3106 .getParcelable(Notification.EXTRA_MEDIA_REMOTE_INTENT)); 3107 } 3108 3109 @Test testCustomMediaStyleRemotePlayback_noPermission()3110 public void testCustomMediaStyleRemotePlayback_noPermission() throws Exception { 3111 int id = 99; 3112 final String deviceName = "device name"; 3113 final int deviceIcon = 123; 3114 final PendingIntent deviceIntent = getPendingIntent(); 3115 final Notification notification = 3116 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 3117 .setSmallIcon(R.drawable.black) 3118 .setStyle(new Notification.DecoratedMediaCustomViewStyle() 3119 .setRemotePlaybackInfo(deviceName, deviceIcon, deviceIntent)) 3120 .build(); 3121 mNotificationManager.notify(id, notification); 3122 3123 StatusBarNotification sbn = mNotificationHelper.findPostedNotification( 3124 null, id, SEARCH_TYPE.APP); 3125 assertNotNull(sbn); 3126 3127 assertFalse(sbn.getNotification().extras 3128 .containsKey(Notification.EXTRA_MEDIA_REMOTE_DEVICE)); 3129 assertFalse(sbn.getNotification().extras 3130 .containsKey(Notification.EXTRA_MEDIA_REMOTE_ICON)); 3131 assertFalse(sbn.getNotification().extras 3132 .containsKey(Notification.EXTRA_MEDIA_REMOTE_INTENT)); 3133 } 3134 3135 @Test testCustomMediaStyleRemotePlayback_hasPermission()3136 public void testCustomMediaStyleRemotePlayback_hasPermission() throws Exception { 3137 int id = 99; 3138 final String deviceName = "device name"; 3139 final int deviceIcon = 123; 3140 final PendingIntent deviceIntent = getPendingIntent(); 3141 final Notification notification = 3142 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 3143 .setSmallIcon(R.drawable.black) 3144 .setStyle(new Notification.DecoratedMediaCustomViewStyle() 3145 .setRemotePlaybackInfo(deviceName, deviceIcon, deviceIntent)) 3146 .build(); 3147 3148 SystemUtil.runWithShellPermissionIdentity(() -> { 3149 mNotificationManager.notify(id, notification); 3150 }, android.Manifest.permission.MEDIA_CONTENT_CONTROL); 3151 3152 StatusBarNotification sbn = mNotificationHelper.findPostedNotification( 3153 null, id, SEARCH_TYPE.APP); 3154 assertNotNull(sbn); 3155 assertEquals(deviceName, sbn.getNotification().extras 3156 .getString(Notification.EXTRA_MEDIA_REMOTE_DEVICE)); 3157 assertEquals(deviceIcon, sbn.getNotification().extras 3158 .getInt(Notification.EXTRA_MEDIA_REMOTE_ICON)); 3159 assertEquals(deviceIntent, sbn.getNotification().extras 3160 .getParcelable(Notification.EXTRA_MEDIA_REMOTE_INTENT)); 3161 } 3162 3163 @Test testNoPermission()3164 public void testNoPermission() throws Exception { 3165 int id = 7; 3166 SystemUtil.runWithShellPermissionIdentity( 3167 () -> mContext.getSystemService(PermissionManager.class) 3168 .revokePostNotificationPermissionWithoutKillForTest( 3169 mContext.getPackageName(), 3170 android.os.Process.myUserHandle().getIdentifier()), 3171 REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL, 3172 REVOKE_RUNTIME_PERMISSIONS); 3173 3174 final Notification notification = 3175 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 3176 .setSmallIcon(R.drawable.black) 3177 .build(); 3178 mNotificationManager.notify(id, notification); 3179 3180 assertTrue(mNotificationHelper.isNotificationGone(id, SEARCH_TYPE.APP)); 3181 } 3182 3183 @Test testIsAmbient()3184 public void testIsAmbient() throws Exception { 3185 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 3186 assertNotNull(mListener); 3187 CountDownLatch postedLatch = mListener.setPostedCountDown(2); 3188 3189 NotificationChannel lowChannel = new NotificationChannel( 3190 "testIsAmbientLOW", "testIsAmbientLOW", IMPORTANCE_LOW); 3191 NotificationChannel minChannel = new NotificationChannel( 3192 "testIsAmbientMIN", "testIsAmbientMIN", IMPORTANCE_MIN); 3193 mNotificationManager.createNotificationChannel(lowChannel); 3194 mNotificationManager.createNotificationChannel(minChannel); 3195 3196 final Notification lowN = 3197 new Notification.Builder(mContext, lowChannel.getId()) 3198 .setSmallIcon(R.drawable.black) 3199 .build(); 3200 final Notification minN = 3201 new Notification.Builder(mContext, minChannel.getId()) 3202 .setSmallIcon(R.drawable.black) 3203 .build(); 3204 mNotificationManager.notify("lowN", 1, lowN); 3205 mNotificationManager.notify("minN", 1, minN); 3206 3207 postedLatch.await(POST_TIMEOUT, TimeUnit.MILLISECONDS); 3208 StatusBarNotification lowSbn = mNotificationHelper.findPostedNotification("lowN", 1, 3209 SEARCH_TYPE.POSTED); 3210 StatusBarNotification minSbn = mNotificationHelper.findPostedNotification("minN", 1, 3211 SEARCH_TYPE.POSTED); 3212 3213 NotificationListenerService.RankingMap rankingMap = mListener.mRankingMap; 3214 NotificationListenerService.Ranking outRanking = new NotificationListenerService.Ranking(); 3215 3216 rankingMap.getRanking(lowSbn.getKey(), outRanking); 3217 assertFalse(outRanking.isAmbient()); 3218 3219 rankingMap.getRanking(minSbn.getKey(), outRanking); 3220 assertEquals(outRanking.getKey(), IMPORTANCE_MIN, outRanking.getChannel().getImportance()); 3221 assertTrue(outRanking.isAmbient()); 3222 } 3223 3224 @Test testFlagForegroundServiceNeedsRealFgs()3225 public void testFlagForegroundServiceNeedsRealFgs() throws Exception { 3226 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 3227 assertNotNull(mListener); 3228 CountDownLatch postedLatch = mListener.setPostedCountDown(1); 3229 3230 final Notification n = 3231 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 3232 .setSmallIcon(R.drawable.black) 3233 .setFlag(FLAG_FOREGROUND_SERVICE, true) 3234 .build(); 3235 mNotificationManager.notify("testFlagForegroundServiceNeedsRealFgs", 1, n); 3236 postedLatch.await(POST_TIMEOUT, TimeUnit.MILLISECONDS); 3237 StatusBarNotification sbn = mNotificationHelper.findPostedNotification( 3238 "testFlagForegroundServiceNeedsRealFgs", 1, SEARCH_TYPE.POSTED); 3239 3240 assertEquals(0, (sbn.getNotification().flags & FLAG_FOREGROUND_SERVICE)); 3241 } 3242 3243 @Test testFlagUserInitiatedJobNeedsRealUij()3244 public void testFlagUserInitiatedJobNeedsRealUij() throws Exception { 3245 mListener = mNotificationHelper.enableListener(STUB_PACKAGE_NAME); 3246 assertNotNull(mListener); 3247 CountDownLatch postedLatch = mListener.setPostedCountDown(1); 3248 3249 final Notification n = 3250 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 3251 .setSmallIcon(R.drawable.black) 3252 .setFlag(FLAG_USER_INITIATED_JOB, true) 3253 .build(); 3254 mNotificationManager.notify("testFlagUserInitiatedJobNeedsRealUij", 1, n); 3255 postedLatch.await(POST_TIMEOUT, TimeUnit.MILLISECONDS); 3256 StatusBarNotification sbn = mNotificationHelper.findPostedNotification( 3257 "testFlagUserInitiatedJobNeedsRealUij", 1, SEARCH_TYPE.POSTED); 3258 3259 assertFalse(sbn.getNotification().isUserInitiatedJob()); 3260 } 3261 3262 @Test 3263 @RequiresFlagsEnabled(android.service.notification.Flags.FLAG_CALLSTYLE_CALLBACK_API) testCallNotificationListener_registerCallback_noInteractAcrossUsersPermission()3264 public void testCallNotificationListener_registerCallback_noInteractAcrossUsersPermission() 3265 throws Exception { 3266 try { 3267 PermissionUtils.revokePermission(mContext.getPackageName(), 3268 android.Manifest.permission.INTERACT_ACROSS_USERS); 3269 3270 mNotificationManager.registerCallNotificationEventListener(mContext.getPackageName(), 3271 UserHandle.SYSTEM, mContext.getMainExecutor(), 3272 new CallNotificationEventListener() { 3273 @Override 3274 public void onCallNotificationPosted(String packageName, UserHandle user) { 3275 } 3276 @Override 3277 public void onCallNotificationRemoved(String packageName, UserHandle user) { 3278 } 3279 }); 3280 fail("registerCallNotificationListener should not succeed - privileged call"); 3281 } catch (SecurityException e) { 3282 // Expected SecurityException 3283 } finally { 3284 PermissionUtils.grantPermission(mContext.getPackageName(), 3285 android.Manifest.permission.INTERACT_ACROSS_USERS); 3286 } 3287 } 3288 3289 @Test 3290 @RequiresFlagsEnabled(android.service.notification.Flags.FLAG_CALLSTYLE_CALLBACK_API) testCallNotificationListener_registerCallback_noAccessNotificationsPermission()3291 public void testCallNotificationListener_registerCallback_noAccessNotificationsPermission() 3292 throws Exception { 3293 try { 3294 PermissionUtils.setAppOp(mContext.getPackageName(), 3295 android.Manifest.permission.ACCESS_NOTIFICATIONS, MODE_ERRORED); 3296 3297 mNotificationManager.registerCallNotificationEventListener(mContext.getPackageName(), 3298 UserHandle.SYSTEM, mContext.getMainExecutor(), 3299 new CallNotificationEventListener() { 3300 @Override 3301 public void onCallNotificationPosted(String packageName, UserHandle user) { 3302 } 3303 @Override 3304 public void onCallNotificationRemoved(String packageName, UserHandle user) { 3305 } 3306 }); 3307 fail("registerCallNotificationListener should not succeed - privileged call"); 3308 } catch (SecurityException e) { 3309 // Expected SecurityException 3310 } finally { 3311 PermissionUtils.setAppOp(mContext.getPackageName(), 3312 android.Manifest.permission.ACCESS_NOTIFICATIONS, MODE_DEFAULT); 3313 } 3314 } 3315 @Test 3316 @RequiresFlagsEnabled(android.service.notification.Flags.FLAG_CALLSTYLE_CALLBACK_API) testCallNotificationListener_registerCallback_withPermission()3317 public void testCallNotificationListener_registerCallback_withPermission() 3318 throws Exception { 3319 try { 3320 mNotificationManager.registerCallNotificationEventListener(mContext.getPackageName(), 3321 UserHandle.SYSTEM, mContext.getMainExecutor(), 3322 new CallNotificationEventListener() { 3323 @Override 3324 public void onCallNotificationPosted(@NonNull String packageName, 3325 UserHandle user) { 3326 } 3327 @Override 3328 public void onCallNotificationRemoved(@NonNull String packageName, 3329 UserHandle user) { 3330 } 3331 }); 3332 } catch (SecurityException e) { 3333 fail("registerCallNotificationListener should succeed " + e); 3334 } 3335 } 3336 3337 @Test 3338 @RequiresFlagsEnabled(android.service.notification.Flags.FLAG_CALLSTYLE_CALLBACK_API) testCallNotificationListener_callstyleNotificationPosted()3339 public void testCallNotificationListener_callstyleNotificationPosted() throws Exception { 3340 mNotificationManager.cancelAll(); 3341 final Semaphore semaphore = new Semaphore(0); 3342 try { 3343 mNotificationManager.registerCallNotificationEventListener(mContext.getPackageName(), 3344 UserHandle.CURRENT, mContext.getMainExecutor(), 3345 new CallNotificationEventListener() { 3346 @Override 3347 public void onCallNotificationPosted(String packageName, UserHandle userH) { 3348 semaphore.release(); 3349 } 3350 @Override 3351 public void onCallNotificationRemoved(String packageName, UserHandle user) { 3352 semaphore.release(); 3353 } 3354 }); 3355 } catch (SecurityException e) { 3356 fail("registerCallNotificationListener should succeed " + e); 3357 } 3358 3359 // Post a CallStyle notification 3360 final int id = 4242; 3361 mNotificationManager.notify(id, getCallStyleNotification(id) 3362 .setFullScreenIntent(getPendingIntent(), false).build()); 3363 3364 // Check that onCallNotificationPosted is called 3365 try { 3366 if (!semaphore.tryAcquire(5, TimeUnit.SECONDS)) { 3367 fail("onCallNotificationPosted notification callback failed"); 3368 } 3369 } catch (InterruptedException e) { 3370 fail("notification callback failed " + e); 3371 } 3372 3373 // Check that onCallNotificationRemoved is called 3374 semaphore.release(); 3375 mNotificationManager.cancel(id); 3376 try { 3377 if (!semaphore.tryAcquire(5, TimeUnit.SECONDS)) { 3378 fail("onCallNotificationRemoved notification callback failed"); 3379 } 3380 } catch (InterruptedException e) { 3381 fail("notification callback failed " + e); 3382 } 3383 } 3384 3385 @Test 3386 @RequiresFlagsEnabled(android.service.notification.Flags.FLAG_CALLSTYLE_CALLBACK_API) testCallNotificationListener_nonCallStyleNotificationPosted()3387 public void testCallNotificationListener_nonCallStyleNotificationPosted() 3388 throws Exception { 3389 final Semaphore semaphore = new Semaphore(0); 3390 try { 3391 mNotificationManager.registerCallNotificationEventListener(mContext.getPackageName(), 3392 UserHandle.CURRENT, mContext.getMainExecutor(), 3393 new CallNotificationEventListener() { 3394 @Override 3395 public void onCallNotificationPosted(String packageName, UserHandle user) { 3396 semaphore.release(); 3397 } 3398 @Override 3399 public void onCallNotificationRemoved(String packageName, UserHandle user) { 3400 } 3401 }); 3402 } catch (SecurityException e) { 3403 fail("registerCallNotificationListener should succeed " + e); 3404 } 3405 3406 // Post a non-CallStyle (conversation) notification 3407 final int id = 4242; 3408 mNotificationManager.notify(id, getConversationNotification().build()); 3409 3410 // Check that CallNotificationListener is not called 3411 try { 3412 if (semaphore.tryAcquire(5, TimeUnit.SECONDS)) { 3413 fail("notification callback should fail!"); 3414 } 3415 } catch (InterruptedException e) { 3416 } 3417 } 3418 3419 @Test 3420 @RequiresFlagsEnabled(android.service.notification.Flags.FLAG_CALLSTYLE_CALLBACK_API) testCallNotificationListener_unregisterListener()3421 public void testCallNotificationListener_unregisterListener() throws Exception { 3422 final Semaphore semaphore = new Semaphore(0); 3423 final CallNotificationEventListener listener = new CallNotificationEventListener() { 3424 @Override 3425 public void onCallNotificationPosted(String packageName, UserHandle user) { 3426 semaphore.release(); 3427 } 3428 @Override 3429 public void onCallNotificationRemoved(String packageName, UserHandle user) { 3430 } 3431 }; 3432 3433 try { 3434 mNotificationManager.registerCallNotificationEventListener(mContext.getPackageName(), 3435 UserHandle.CURRENT, mContext.getMainExecutor(), listener); 3436 } catch (SecurityException e) { 3437 fail("registerCallNotificationListener should succeed " + e); 3438 } 3439 3440 try { 3441 mNotificationManager.unregisterCallNotificationEventListener(listener); 3442 } catch (SecurityException e) { 3443 fail("unregisterCallNotificationListener should succeed " + e); 3444 } 3445 3446 // Post a CallStyle notification 3447 final int id = 4242; 3448 mNotificationManager.notify(id, getCallStyleNotification(id) 3449 .setFullScreenIntent(getPendingIntent(), false).build()); 3450 3451 // Check that onCallNotificationPosted is not called 3452 try { 3453 if (semaphore.tryAcquire(5, TimeUnit.SECONDS)) { 3454 fail("notification callback should fail!"); 3455 } 3456 } catch (InterruptedException e) { 3457 } 3458 } 3459 3460 private static class EventCallback extends Handler { 3461 private static final int BROADCAST_RECEIVED = 1; 3462 private static final int SERVICE_STARTED = 2; 3463 private static final int ACTIVITY_STARTED = 3; 3464 private static final int NOTIFICATION_CLICKED = 4; 3465 3466 private final Map<Integer, CompletableFuture<Integer>> mEvents = 3467 Collections.synchronizedMap(new ArrayMap<>()); 3468 EventCallback()3469 private EventCallback() { 3470 super(Looper.getMainLooper()); 3471 } 3472 3473 @Override handleMessage(Message message)3474 public void handleMessage(Message message) { 3475 mEvents.computeIfAbsent(message.what, e -> new CompletableFuture<>()).obtrudeValue( 3476 message.arg1); 3477 } 3478 waitFor(int event, long timeoutMs)3479 public boolean waitFor(int event, long timeoutMs) { 3480 try { 3481 return mEvents.computeIfAbsent(event, e -> new CompletableFuture<>()).get(timeoutMs, 3482 TimeUnit.MILLISECONDS) == 0; 3483 } catch (InterruptedException | ExecutionException | TimeoutException e) { 3484 return false; 3485 } 3486 } 3487 } 3488 } 3489