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