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.cts; 18 19 import static android.app.Notification.FLAG_BUBBLE; 20 import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL; 21 import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE; 22 import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED; 23 import static android.app.NotificationManager.IMPORTANCE_DEFAULT; 24 import static android.app.NotificationManager.IMPORTANCE_HIGH; 25 import static android.app.NotificationManager.IMPORTANCE_LOW; 26 import static android.app.NotificationManager.IMPORTANCE_MIN; 27 import static android.app.NotificationManager.IMPORTANCE_NONE; 28 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; 29 import static android.app.NotificationManager.INTERRUPTION_FILTER_ALARMS; 30 import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL; 31 import static android.app.NotificationManager.INTERRUPTION_FILTER_NONE; 32 import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY; 33 import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_ANYONE; 34 import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_NONE; 35 import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS; 36 import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CALLS; 37 import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CONVERSATIONS; 38 import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_EVENTS; 39 import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA; 40 import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES; 41 import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_REMINDERS; 42 import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_SYSTEM; 43 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT; 44 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT; 45 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS; 46 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST; 47 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; 48 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF; 49 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON; 50 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR; 51 import static android.app.cts.android.app.cts.tools.NotificationHelper.MAX_WAIT_TIME; 52 import static android.app.cts.android.app.cts.tools.NotificationHelper.SHORT_WAIT_TIME; 53 import static android.app.stubs.BubblesTestService.EXTRA_TEST_CASE; 54 import static android.app.stubs.BubblesTestService.TEST_CALL; 55 import static android.app.stubs.BubblesTestService.TEST_MESSAGING; 56 import static android.app.stubs.SendBubbleActivity.BUBBLE_NOTIF_ID; 57 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK; 58 import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT; 59 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; 60 import static android.content.pm.PackageManager.FEATURE_WATCH; 61 62 import static org.hamcrest.CoreMatchers.hasItem; 63 import static org.junit.Assert.assertThat; 64 import static org.junit.Assert.assertThrows; 65 66 import android.Manifest; 67 import android.app.ActivityManager; 68 import android.app.ActivityOptions; 69 import android.app.AutomaticZenRule; 70 import android.app.Instrumentation; 71 import android.app.KeyguardManager; 72 import android.app.Notification; 73 import android.app.NotificationChannel; 74 import android.app.NotificationChannelGroup; 75 import android.app.NotificationManager; 76 import android.app.NotificationManager.Policy; 77 import android.app.PendingIntent; 78 import android.app.Person; 79 import android.app.UiAutomation; 80 import android.app.cts.android.app.cts.tools.FutureServiceConnection; 81 import android.app.cts.android.app.cts.tools.NotificationHelper; 82 import android.app.role.RoleManager; 83 import android.app.stubs.AutomaticZenRuleActivity; 84 import android.app.stubs.BubbledActivity; 85 import android.app.stubs.BubblesTestService; 86 import android.app.stubs.R; 87 import android.app.stubs.SendBubbleActivity; 88 import android.app.stubs.TestNotificationListener; 89 import android.content.BroadcastReceiver; 90 import android.content.ComponentName; 91 import android.content.ContentProviderOperation; 92 import android.content.ContentResolver; 93 import android.content.Context; 94 import android.content.Intent; 95 import android.content.IntentFilter; 96 import android.content.LocusId; 97 import android.content.OperationApplicationException; 98 import android.content.ServiceConnection; 99 import android.content.pm.PackageInfo; 100 import android.content.pm.PackageManager; 101 import android.content.pm.ShortcutInfo; 102 import android.content.pm.ShortcutManager; 103 import android.content.res.AssetFileDescriptor; 104 import android.database.Cursor; 105 import android.graphics.Bitmap; 106 import android.graphics.drawable.Icon; 107 import android.media.AudioAttributes; 108 import android.media.AudioManager; 109 import android.media.session.MediaSession; 110 import android.net.Uri; 111 import android.os.Build; 112 import android.os.Bundle; 113 import android.os.Handler; 114 import android.os.IBinder; 115 import android.os.Looper; 116 import android.os.Message; 117 import android.os.Messenger; 118 import android.os.ParcelFileDescriptor; 119 import android.os.RemoteException; 120 import android.os.SystemClock; 121 import android.os.UserHandle; 122 import android.platform.test.annotations.AsbSecurityTest; 123 import android.provider.ContactsContract; 124 import android.provider.ContactsContract.CommonDataKinds.Email; 125 import android.provider.ContactsContract.CommonDataKinds.Phone; 126 import android.provider.ContactsContract.CommonDataKinds.StructuredName; 127 import android.provider.ContactsContract.Data; 128 import android.provider.Settings; 129 import android.provider.Telephony.Threads; 130 import android.service.notification.Condition; 131 import android.service.notification.NotificationListenerService; 132 import android.service.notification.StatusBarNotification; 133 import android.service.notification.ZenPolicy; 134 import android.support.test.uiautomator.UiDevice; 135 import android.test.AndroidTestCase; 136 import android.util.ArrayMap; 137 import android.util.ArraySet; 138 import android.util.Log; 139 import android.widget.RemoteViews; 140 141 import androidx.annotation.NonNull; 142 import androidx.annotation.Nullable; 143 import androidx.test.platform.app.InstrumentationRegistry; 144 145 import com.android.compatibility.common.util.FeatureUtil; 146 import com.android.compatibility.common.util.PollingCheck; 147 import com.android.compatibility.common.util.SystemUtil; 148 import com.android.compatibility.common.util.ThrowingSupplier; 149 import com.android.test.notificationlistener.INotificationUriAccessService; 150 151 import com.google.common.base.Preconditions; 152 153 import java.io.BufferedReader; 154 import java.io.FileInputStream; 155 import java.io.IOException; 156 import java.io.InputStream; 157 import java.io.InputStreamReader; 158 import java.util.ArrayList; 159 import java.util.Arrays; 160 import java.util.Collections; 161 import java.util.HashMap; 162 import java.util.List; 163 import java.util.Map; 164 import java.util.Objects; 165 import java.util.Set; 166 import java.util.UUID; 167 import java.util.concurrent.CompletableFuture; 168 import java.util.concurrent.CompletionException; 169 import java.util.concurrent.CountDownLatch; 170 import java.util.concurrent.ExecutionException; 171 import java.util.concurrent.Executor; 172 import java.util.concurrent.Semaphore; 173 import java.util.concurrent.TimeUnit; 174 import java.util.concurrent.TimeoutException; 175 176 /* This tests NotificationListenerService together with NotificationManager, as you need to have 177 * notifications to manipulate in order to test the listener service. */ 178 public class NotificationManagerTest extends AndroidTestCase { 179 public static final String NOTIFICATIONPROVIDER = "com.android.test.notificationprovider"; 180 public static final String RICH_NOTIFICATION_ACTIVITY = 181 "com.android.test.notificationprovider.RichNotificationActivity"; 182 final String TAG = NotificationManagerTest.class.getSimpleName(); 183 final boolean DEBUG = false; 184 static final String NOTIFICATION_CHANNEL_ID = "NotificationManagerTest"; 185 186 private static final String DELEGATOR = "com.android.test.notificationdelegator"; 187 private static final String DELEGATE_POST_CLASS = DELEGATOR + ".NotificationDelegateAndPost"; 188 private static final String REVOKE_CLASS = DELEGATOR + ".NotificationRevoker"; 189 private static final String SHARE_SHORTCUT_ID = "shareShortcut"; 190 private static final String SHARE_SHORTCUT_CATEGORY = 191 "android.app.stubs.SHARE_SHORTCUT_CATEGORY"; 192 // use a value of 10000 for consistency with other CTS tests (see 193 // android.server.wm.intentLaunchRunner#ACTIVITY_LAUNCH_TIMEOUT) 194 private static final int ACTIVITY_LAUNCH_TIMEOUT = 10000; 195 196 private static final String TRAMPOLINE_APP = 197 "com.android.test.notificationtrampoline.current"; 198 private static final String TRAMPOLINE_APP_API_30 = 199 "com.android.test.notificationtrampoline.api30"; 200 private static final ComponentName TRAMPOLINE_SERVICE = 201 new ComponentName(TRAMPOLINE_APP, 202 "com.android.test.notificationtrampoline.NotificationTrampolineTestService"); 203 private static final ComponentName TRAMPOLINE_SERVICE_API_30 = 204 new ComponentName(TRAMPOLINE_APP_API_30, 205 "com.android.test.notificationtrampoline.NotificationTrampolineTestService"); 206 207 private static final long TIMEOUT_LONG_MS = 10000; 208 private static final long TIMEOUT_MS = 4000; 209 private static final int MESSAGE_BROADCAST_NOTIFICATION = 1; 210 private static final int MESSAGE_SERVICE_NOTIFICATION = 2; 211 private static final int MESSAGE_CLICK_NOTIFICATION = 3; 212 213 private PackageManager mPackageManager; 214 private AudioManager mAudioManager; 215 private RoleManager mRoleManager; 216 private NotificationManager mNotificationManager; 217 private ActivityManager mActivityManager; 218 private String mId; 219 private TestNotificationListener mListener; 220 private List<String> mRuleIds; 221 private BroadcastReceiver mBubbleBroadcastReceiver; 222 private boolean mBubblesEnabledSettingToRestore; 223 private INotificationUriAccessService mNotificationUriAccessService; 224 private FutureServiceConnection mTrampolineConnection; 225 private NotificationHelper mNotificationHelper; 226 227 @Nullable 228 private List<String> mPreviousDefaultBrowser; 229 private Instrumentation mInstrumentation; 230 231 @Override setUp()232 protected void setUp() throws Exception { 233 super.setUp(); 234 // This will leave a set of channels on the device with each test run. 235 mId = UUID.randomUUID().toString(); 236 mNotificationManager = (NotificationManager) mContext.getSystemService( 237 Context.NOTIFICATION_SERVICE); 238 mNotificationHelper = new NotificationHelper(mContext, () -> mListener); 239 // clear the deck so that our getActiveNotifications results are predictable 240 mNotificationManager.cancelAll(); 241 242 assertEquals("Previous test left system in a bad state", 243 0, mNotificationManager.getActiveNotifications().length); 244 245 mNotificationManager.createNotificationChannel(new NotificationChannel( 246 NOTIFICATION_CHANNEL_ID, "name", IMPORTANCE_DEFAULT)); 247 mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); 248 mPackageManager = mContext.getPackageManager(); 249 mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); 250 mRoleManager = mContext.getSystemService(RoleManager.class); 251 mRuleIds = new ArrayList<>(); 252 253 // ensure listener access isn't allowed before test runs (other tests could put 254 // TestListener in an unexpected state) 255 toggleListenerAccess(false); 256 mInstrumentation = InstrumentationRegistry.getInstrumentation(); 257 toggleNotificationPolicyAccess(mContext.getPackageName(), mInstrumentation, true); 258 mNotificationManager.setInterruptionFilter(INTERRUPTION_FILTER_ALL); 259 toggleNotificationPolicyAccess(mContext.getPackageName(), mInstrumentation, false); 260 261 // This setting is forced on / off for certain tests, save it & restore what's on the 262 // device after tests are run 263 mBubblesEnabledSettingToRestore = Settings.Secure.getInt(mContext.getContentResolver(), 264 Settings.Secure.NOTIFICATION_BUBBLES) == 1; 265 266 // Ensure that the tests are exempt from global service-related rate limits 267 setEnableServiceNotificationRateLimit(false); 268 269 // delay between tests so notifications aren't dropped by the rate limiter 270 try { 271 Thread.sleep(500); 272 } catch (InterruptedException e) { 273 } 274 } 275 276 @Override tearDown()277 protected void tearDown() throws Exception { 278 super.tearDown(); 279 280 setEnableServiceNotificationRateLimit(true); 281 282 mNotificationManager.cancelAll(); 283 for (String id : mRuleIds) { 284 mNotificationManager.removeAutomaticZenRule(id); 285 } 286 287 assertExpectedDndState(INTERRUPTION_FILTER_ALL); 288 289 List<NotificationChannel> channels = mNotificationManager.getNotificationChannels(); 290 // Delete all channels. 291 for (NotificationChannel nc : channels) { 292 if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(nc.getId())) { 293 continue; 294 } 295 mNotificationManager.deleteNotificationChannel(nc.getId()); 296 } 297 298 // Unsuspend package if it was suspended in the test 299 suspendPackage(mContext.getPackageName(), mInstrumentation, false); 300 301 toggleListenerAccess(false); 302 toggleNotificationPolicyAccess(mContext.getPackageName(), mInstrumentation, false); 303 304 List<NotificationChannelGroup> groups = mNotificationManager.getNotificationChannelGroups(); 305 // Delete all groups. 306 for (NotificationChannelGroup ncg : groups) { 307 mNotificationManager.deleteNotificationChannelGroup(ncg.getId()); 308 } 309 310 // Restore bubbles setting 311 setBubblesGlobal(mBubblesEnabledSettingToRestore); 312 313 // For trampoline tests 314 if (mTrampolineConnection != null) { 315 mContext.unbindService(mTrampolineConnection); 316 mTrampolineConnection = null; 317 } 318 if (mListener != null) { 319 mListener.removeTestPackage(TRAMPOLINE_APP_API_30); 320 mListener.removeTestPackage(TRAMPOLINE_APP); 321 } 322 if (mPreviousDefaultBrowser != null) { 323 restoreDefaultBrowser(); 324 } 325 } 326 assertNotificationCancelled(int id, boolean all)327 private void assertNotificationCancelled(int id, boolean all) { 328 for (long totalWait = 0; totalWait < MAX_WAIT_TIME; totalWait += SHORT_WAIT_TIME) { 329 StatusBarNotification sbn = findNotificationNoWait(id, all); 330 if (sbn == null) return; 331 try { 332 Thread.sleep(SHORT_WAIT_TIME); 333 } catch (InterruptedException e) { 334 e.printStackTrace(); 335 } 336 } 337 assertNull(findNotificationNoWait(id, all)); 338 } 339 insertSingleContact(String name, String phone, String email, boolean starred)340 private void insertSingleContact(String name, String phone, String email, boolean starred) { 341 final ArrayList<ContentProviderOperation> operationList = 342 new ArrayList<ContentProviderOperation>(); 343 ContentProviderOperation.Builder builder = 344 ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI); 345 builder.withValue(ContactsContract.RawContacts.STARRED, starred ? 1 : 0); 346 operationList.add(builder.build()); 347 348 builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); 349 builder.withValueBackReference(StructuredName.RAW_CONTACT_ID, 0); 350 builder.withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE); 351 builder.withValue(StructuredName.DISPLAY_NAME, name); 352 operationList.add(builder.build()); 353 354 if (phone != null) { 355 builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); 356 builder.withValueBackReference(Phone.RAW_CONTACT_ID, 0); 357 builder.withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 358 builder.withValue(Phone.TYPE, Phone.TYPE_MOBILE); 359 builder.withValue(Phone.NUMBER, phone); 360 builder.withValue(Data.IS_PRIMARY, 1); 361 operationList.add(builder.build()); 362 } 363 if (email != null) { 364 builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); 365 builder.withValueBackReference(Email.RAW_CONTACT_ID, 0); 366 builder.withValue(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); 367 builder.withValue(Email.TYPE, Email.TYPE_HOME); 368 builder.withValue(Email.DATA, email); 369 operationList.add(builder.build()); 370 } 371 372 try { 373 mContext.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operationList); 374 } catch (RemoteException e) { 375 Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); 376 } catch (OperationApplicationException e) { 377 Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); 378 } 379 } 380 deleteSingleContact(Uri uri)381 private void deleteSingleContact(Uri uri) { 382 final ArrayList<ContentProviderOperation> operationList = 383 new ArrayList<ContentProviderOperation>(); 384 operationList.add(ContentProviderOperation.newDelete(uri).build()); 385 try { 386 mContext.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operationList); 387 } catch (RemoteException e) { 388 Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); 389 } catch (OperationApplicationException e) { 390 Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); 391 } 392 } 393 lookupContact(String phone)394 private Uri lookupContact(String phone) { 395 Cursor c = null; 396 try { 397 Uri phoneUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, 398 Uri.encode(phone)); 399 String[] projection = new String[]{ContactsContract.Contacts._ID, 400 ContactsContract.Contacts.LOOKUP_KEY}; 401 c = mContext.getContentResolver().query(phoneUri, projection, null, null, null); 402 if (c != null && c.getCount() > 0) { 403 c.moveToFirst(); 404 int lookupIdx = c.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY); 405 int idIdx = c.getColumnIndex(ContactsContract.Contacts._ID); 406 String lookupKey = c.getString(lookupIdx); 407 long contactId = c.getLong(idIdx); 408 return ContactsContract.Contacts.getLookupUri(contactId, lookupKey); 409 } 410 } catch (Throwable t) { 411 Log.w(TAG, "Problem getting content resolver or performing contacts query.", t); 412 } finally { 413 if (c != null) { 414 c.close(); 415 } 416 } 417 return null; 418 } 419 findPostedNotification(int id, boolean all)420 private StatusBarNotification findPostedNotification(int id, boolean all) { 421 return mNotificationHelper.findPostedNotification(id, all); 422 } 423 findNotificationNoWait(int id, boolean all)424 private StatusBarNotification findNotificationNoWait(int id, boolean all) { 425 return mNotificationHelper.findNotificationNoWait(id, all); 426 } 427 getActiveNotifications(boolean all)428 private StatusBarNotification[] getActiveNotifications(boolean all) { 429 return mNotificationHelper.getActiveNotifications(all); 430 } 431 getPendingIntent()432 private PendingIntent getPendingIntent() { 433 return PendingIntent.getActivity( 434 getContext(), 0, new Intent(getContext(), this.getClass()), PendingIntent.FLAG_MUTABLE_UNAUDITED); 435 } 436 isGroupSummary(Notification n)437 private boolean isGroupSummary(Notification n) { 438 return n.getGroup() != null && (n.flags & Notification.FLAG_GROUP_SUMMARY) != 0; 439 } 440 assertOnlySomeNotificationsAutogrouped(List<Integer> autoGroupedIds)441 private void assertOnlySomeNotificationsAutogrouped(List<Integer> autoGroupedIds) { 442 String expectedGroupKey = null; 443 try { 444 // Posting can take ~100 ms 445 Thread.sleep(150); 446 } catch (InterruptedException e) { 447 e.printStackTrace(); 448 } 449 StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications(); 450 for (StatusBarNotification sbn : sbns) { 451 if (isGroupSummary(sbn.getNotification()) 452 || autoGroupedIds.contains(sbn.getId())) { 453 assertTrue(sbn.getKey() + " is unexpectedly not autogrouped", 454 sbn.getOverrideGroupKey() != null); 455 if (expectedGroupKey == null) { 456 expectedGroupKey = sbn.getGroupKey(); 457 } 458 assertEquals(expectedGroupKey, sbn.getGroupKey()); 459 } else { 460 assertTrue(sbn.isGroup()); 461 assertTrue(sbn.getKey() + " is unexpectedly autogrouped,", 462 sbn.getOverrideGroupKey() == null); 463 assertTrue(sbn.getKey() + " has an unusual group key", 464 sbn.getGroupKey() != expectedGroupKey); 465 } 466 } 467 } 468 assertAllPostedNotificationsAutogrouped()469 private void assertAllPostedNotificationsAutogrouped() { 470 String expectedGroupKey = null; 471 try { 472 // Posting can take ~100 ms 473 Thread.sleep(150); 474 } catch (InterruptedException e) { 475 e.printStackTrace(); 476 } 477 StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications(); 478 for (StatusBarNotification sbn : sbns) { 479 // all notis should be in a group determined by autogrouping 480 assertTrue(sbn.getOverrideGroupKey() != null); 481 if (expectedGroupKey == null) { 482 expectedGroupKey = sbn.getGroupKey(); 483 } 484 // all notis should be in the same group 485 assertEquals(expectedGroupKey, sbn.getGroupKey()); 486 } 487 } 488 cancelAndPoll(int id)489 private void cancelAndPoll(int id) { 490 mNotificationManager.cancel(id); 491 492 try { 493 Thread.sleep(500); 494 } catch (InterruptedException ex) { 495 // pass 496 } 497 if (!checkNotificationExistence(id, /*shouldExist=*/ false)) { 498 fail("canceled notification was still alive, id=" + id); 499 } 500 } 501 sendNotification(final int id, final int icon)502 private void sendNotification(final int id, final int icon) throws Exception { 503 sendNotification(id, null, icon); 504 } 505 sendNotification(final int id, String groupKey, final int icon)506 private void sendNotification(final int id, String groupKey, final int icon) throws Exception { 507 final Intent intent = new Intent(Intent.ACTION_MAIN, Threads.CONTENT_URI); 508 509 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP 510 | Intent.FLAG_ACTIVITY_CLEAR_TOP); 511 intent.setAction(Intent.ACTION_MAIN); 512 513 final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 514 PendingIntent.FLAG_MUTABLE); 515 final Notification notification = 516 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 517 .setSmallIcon(icon) 518 .setWhen(System.currentTimeMillis()) 519 .setContentTitle("notify#" + id) 520 .setContentText("This is #" + id + "notification ") 521 .setContentIntent(pendingIntent) 522 .setGroup(groupKey) 523 .build(); 524 mNotificationManager.notify(id, notification); 525 526 if (!checkNotificationExistence(id, /*shouldExist=*/ true)) { 527 fail("couldn't find posted notification id=" + id); 528 } 529 } 530 setUpNotifListener()531 private void setUpNotifListener() { 532 try { 533 toggleListenerAccess(true); 534 mListener = TestNotificationListener.getInstance(); 535 assertNotNull(mListener); 536 mListener.resetData(); 537 } catch (IOException e) { 538 } 539 } 540 sendAndVerifyBubble(final int id, Notification.Builder builder, Notification.BubbleMetadata data, boolean shouldBeBubble)541 private void sendAndVerifyBubble(final int id, Notification.Builder builder, 542 Notification.BubbleMetadata data, boolean shouldBeBubble) { 543 setUpNotifListener(); 544 545 final Intent intent = new Intent(mContext, BubbledActivity.class); 546 547 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP 548 | Intent.FLAG_ACTIVITY_CLEAR_TOP); 549 intent.setAction(Intent.ACTION_MAIN); 550 final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED); 551 552 if (data == null) { 553 data = new Notification.BubbleMetadata.Builder(pendingIntent, 554 Icon.createWithResource(mContext, R.drawable.black)) 555 .build(); 556 } 557 if (builder == null) { 558 builder = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 559 .setSmallIcon(R.drawable.black) 560 .setWhen(System.currentTimeMillis()) 561 .setContentTitle("notify#" + id) 562 .setContentText("This is #" + id + "notification ") 563 .setContentIntent(pendingIntent); 564 } 565 builder.setBubbleMetadata(data); 566 567 Notification notif = builder.build(); 568 mNotificationManager.notify(id, notif); 569 570 verifyNotificationBubbleState(id, shouldBeBubble); 571 } 572 573 /** 574 * Make sure {@link #setUpNotifListener()} is called prior to sending the notif and verifying 575 * in this method. 576 */ verifyNotificationBubbleState(int id, boolean shouldBeBubble)577 private void verifyNotificationBubbleState(int id, boolean shouldBeBubble) { 578 try { 579 // FLAG_BUBBLE relies on notification being posted, wait for notification listener 580 Thread.sleep(500); 581 } catch (InterruptedException ex) { 582 } 583 584 for (StatusBarNotification sbn : mListener.mPosted) { 585 if (sbn.getId() == id) { 586 boolean isBubble = (sbn.getNotification().flags & FLAG_BUBBLE) != 0; 587 if (isBubble != shouldBeBubble) { 588 final String failure = shouldBeBubble 589 ? "Notification with id= " + id + " wasn't a bubble" 590 : "Notification with id= " + id + " was a bubble and shouldn't be"; 591 fail(failure); 592 } else { 593 // pass 594 return; 595 } 596 } 597 } 598 fail("Couldn't find posted notification with id= " + id); 599 } 600 getCancellationReason(String key)601 private int getCancellationReason(String key) { 602 for (int tries = 3; tries-- > 0; ) { 603 if (mListener.mRemoved.containsKey(key)) { 604 return mListener.mRemoved.get(key); 605 } 606 try { 607 Thread.sleep(1000); 608 } catch (InterruptedException ex) { 609 // pass 610 } 611 } 612 return -1; 613 } 614 checkNotificationExistence(int id, boolean shouldExist)615 private boolean checkNotificationExistence(int id, boolean shouldExist) { 616 // notification is a bit asynchronous so it may take a few ms to appear in 617 // getActiveNotifications() 618 // we will check for it for up to 300ms before giving up 619 boolean found = false; 620 for (int tries = 3; tries-- > 0; ) { 621 // Need reset flag. 622 found = false; 623 final StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications(); 624 for (StatusBarNotification sbn : sbns) { 625 Log.d(TAG, "Found " + sbn.getKey()); 626 if (sbn.getId() == id) { 627 found = true; 628 break; 629 } 630 } 631 if (found == shouldExist) break; 632 try { 633 Thread.sleep(100); 634 } catch (InterruptedException ex) { 635 // pass 636 } 637 } 638 return found == shouldExist; 639 } 640 assertNotificationCount(int expectedCount)641 private void assertNotificationCount(int expectedCount) { 642 // notification is a bit asynchronous so it may take a few ms to appear in 643 // getActiveNotifications() 644 // we will check for it for up to 400ms before giving up 645 int lastCount = 0; 646 for (int tries = 4; tries-- > 0; ) { 647 final StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications(); 648 lastCount = sbns.length; 649 if (expectedCount == lastCount) return; 650 try { 651 Thread.sleep(100); 652 } catch (InterruptedException ex) { 653 // pass 654 } 655 } 656 fail("Expected " + expectedCount + " posted notifications, were " + lastCount); 657 } 658 compareChannels(NotificationChannel expected, NotificationChannel actual)659 private void compareChannels(NotificationChannel expected, NotificationChannel actual) { 660 if (actual == null) { 661 fail("actual channel is null"); 662 return; 663 } 664 if (expected == null) { 665 fail("expected channel is null"); 666 return; 667 } 668 assertEquals(expected.getId(), actual.getId()); 669 assertEquals(expected.getName(), actual.getName()); 670 assertEquals(expected.getDescription(), actual.getDescription()); 671 assertEquals(expected.shouldVibrate(), actual.shouldVibrate()); 672 assertEquals(expected.shouldShowLights(), actual.shouldShowLights()); 673 assertEquals(expected.getLightColor(), actual.getLightColor()); 674 assertEquals(expected.getImportance(), actual.getImportance()); 675 if (expected.getSound() == null) { 676 assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actual.getSound()); 677 assertEquals(Notification.AUDIO_ATTRIBUTES_DEFAULT, actual.getAudioAttributes()); 678 } else { 679 assertEquals(expected.getSound(), actual.getSound()); 680 assertEquals(expected.getAudioAttributes(), actual.getAudioAttributes()); 681 } 682 assertTrue(Arrays.equals(expected.getVibrationPattern(), actual.getVibrationPattern())); 683 assertEquals(expected.getGroup(), actual.getGroup()); 684 assertEquals(expected.getConversationId(), actual.getConversationId()); 685 assertEquals(expected.getParentChannelId(), actual.getParentChannelId()); 686 assertEquals(expected.isDemoted(), actual.isDemoted()); 687 } 688 setEnableServiceNotificationRateLimit(boolean enable)689 private void setEnableServiceNotificationRateLimit(boolean enable) throws IOException { 690 String command = "cmd activity fgs-notification-rate-limit " 691 + (enable ? "enable" : "disable"); 692 693 runCommand(command, InstrumentationRegistry.getInstrumentation()); 694 } 695 toggleNotificationPolicyAccess(String packageName, Instrumentation instrumentation, boolean on)696 private void toggleNotificationPolicyAccess(String packageName, 697 Instrumentation instrumentation, boolean on) throws IOException { 698 699 String command = " cmd notification " + (on ? "allow_dnd " : "disallow_dnd ") + packageName; 700 701 runCommand(command, instrumentation); 702 703 NotificationManager nm = mContext.getSystemService(NotificationManager.class); 704 assertEquals("Notification Policy Access Grant is " 705 + nm.isNotificationPolicyAccessGranted() + " not " + on + " for " 706 + packageName, on, nm.isNotificationPolicyAccessGranted()); 707 } 708 suspendPackage(String packageName, Instrumentation instrumentation, boolean suspend)709 private void suspendPackage(String packageName, 710 Instrumentation instrumentation, boolean suspend) throws IOException { 711 int userId = mContext.getUserId(); 712 String command = " cmd package " + (suspend ? "suspend " : "unsuspend ") 713 + "--user " + userId + " " + packageName; 714 715 runCommand(command, instrumentation); 716 } 717 toggleListenerAccess(boolean on)718 private void toggleListenerAccess(boolean on) throws IOException { 719 toggleListenerAccess(mContext, on); 720 } 721 toggleListenerAccess(Context context, boolean on)722 public static void toggleListenerAccess(Context context, boolean on) throws IOException { 723 String command = " cmd notification " + (on ? "allow_listener " : "disallow_listener ") 724 + TestNotificationListener.getId(); 725 726 runCommand(command, InstrumentationRegistry.getInstrumentation()); 727 728 final NotificationManager nm = context.getSystemService(NotificationManager.class); 729 final ComponentName listenerComponent = TestNotificationListener.getComponentName(); 730 assertEquals(listenerComponent + " has incorrect listener access", 731 on, nm.isNotificationListenerAccessGranted(listenerComponent)); 732 } 733 toggleExternalListenerAccess(ComponentName listenerComponent, boolean on)734 private void toggleExternalListenerAccess(ComponentName listenerComponent, boolean on) 735 throws IOException { 736 String command = " cmd notification " + (on ? "allow_listener " : "disallow_listener ") 737 + listenerComponent.flattenToString(); 738 runCommand(command, InstrumentationRegistry.getInstrumentation()); 739 } 740 setBubblesGlobal(boolean enabled)741 private void setBubblesGlobal(boolean enabled) 742 throws InterruptedException { 743 SystemUtil.runWithShellPermissionIdentity(() -> 744 Settings.Secure.putInt(mContext.getContentResolver(), 745 Settings.Secure.NOTIFICATION_BUBBLES, enabled ? 1 : 0)); 746 Thread.sleep(500); // wait for ranking update 747 } 748 setBubblesAppPref(int pref)749 private void setBubblesAppPref(int pref) throws Exception { 750 int userId = mContext.getUser().getIdentifier(); 751 String pkg = mContext.getPackageName(); 752 String command = " cmd notification set_bubbles " + pkg 753 + " " + Integer.toString(pref) 754 + " " + userId; 755 runCommand(command, InstrumentationRegistry.getInstrumentation()); 756 Thread.sleep(500); // wait for ranking update 757 } 758 setBubblesChannelAllowed(boolean allowed)759 private void setBubblesChannelAllowed(boolean allowed) throws Exception { 760 int userId = mContext.getUser().getIdentifier(); 761 String pkg = mContext.getPackageName(); 762 String command = " cmd notification set_bubbles_channel " + pkg 763 + " " + NOTIFICATION_CHANNEL_ID 764 + " " + Boolean.toString(allowed) 765 + " " + userId; 766 runCommand(command, InstrumentationRegistry.getInstrumentation()); 767 Thread.sleep(500); // wait for ranking update 768 } 769 770 @SuppressWarnings("StatementWithEmptyBody") runCommand(String command, Instrumentation instrumentation)771 private static void runCommand(String command, Instrumentation instrumentation) 772 throws IOException { 773 UiAutomation uiAutomation = instrumentation.getUiAutomation(); 774 // Execute command 775 try (ParcelFileDescriptor fd = uiAutomation.executeShellCommand(command)) { 776 assertNotNull("Failed to execute shell command: " + command, fd); 777 // Wait for the command to finish by reading until EOF 778 try (InputStream in = new FileInputStream(fd.getFileDescriptor())) { 779 byte[] buffer = new byte[4096]; 780 while (in.read(buffer) > 0) { 781 // discard output 782 } 783 } catch (IOException e) { 784 throw new IOException("Could not read stdout of command: " + command, e); 785 } 786 } finally { 787 uiAutomation.destroy(); 788 } 789 } 790 areRulesSame(AutomaticZenRule a, AutomaticZenRule b)791 private boolean areRulesSame(AutomaticZenRule a, AutomaticZenRule b) { 792 return a.isEnabled() == b.isEnabled() 793 && Objects.equals(a.getName(), b.getName()) 794 && a.getInterruptionFilter() == b.getInterruptionFilter() 795 && Objects.equals(a.getConditionId(), b.getConditionId()) 796 && Objects.equals(a.getOwner(), b.getOwner()) 797 && Objects.equals(a.getZenPolicy(), b.getZenPolicy()) 798 && Objects.equals(a.getConfigurationActivity(), b.getConfigurationActivity()); 799 } 800 createRule(String name, int filter)801 private AutomaticZenRule createRule(String name, int filter) { 802 return new AutomaticZenRule(name, null, 803 new ComponentName(mContext, AutomaticZenRuleActivity.class), 804 new Uri.Builder().scheme("scheme") 805 .appendPath("path") 806 .appendQueryParameter("fake_rule", "fake_value") 807 .build(), null, filter, true); 808 } 809 createRule(String name)810 private AutomaticZenRule createRule(String name) { 811 return createRule(name, INTERRUPTION_FILTER_PRIORITY); 812 } 813 assertExpectedDndState(int expectedState)814 private void assertExpectedDndState(int expectedState) { 815 int tries = 3; 816 for (int i = tries; i >= 0; i--) { 817 if (expectedState == 818 mNotificationManager.getCurrentInterruptionFilter()) { 819 break; 820 } 821 try { 822 Thread.sleep(100); 823 } catch (InterruptedException e) { 824 e.printStackTrace(); 825 } 826 } 827 828 assertEquals(expectedState, mNotificationManager.getCurrentInterruptionFilter()); 829 } 830 831 /** Creates a dynamic, longlived, sharing shortcut. Call {@link #deleteShortcuts()} after. */ createDynamicShortcut()832 private void createDynamicShortcut() { 833 Person person = new Person.Builder() 834 .setBot(false) 835 .setIcon(Icon.createWithResource(mContext, R.drawable.icon_black)) 836 .setName("BubbleBot") 837 .setImportant(true) 838 .build(); 839 840 Set<String> categorySet = new ArraySet<>(); 841 categorySet.add(SHARE_SHORTCUT_CATEGORY); 842 Intent shortcutIntent = new Intent(mContext, BubbledActivity.class); 843 shortcutIntent.setAction(Intent.ACTION_VIEW); 844 845 ShortcutInfo shortcut = new ShortcutInfo.Builder(mContext, SHARE_SHORTCUT_ID) 846 .setShortLabel(SHARE_SHORTCUT_ID) 847 .setIcon(Icon.createWithResource(mContext, R.drawable.icon_black)) 848 .setIntent(shortcutIntent) 849 .setPerson(person) 850 .setCategories(categorySet) 851 .setLongLived(true) 852 .build(); 853 854 ShortcutManager scManager = 855 (ShortcutManager) mContext.getSystemService(Context.SHORTCUT_SERVICE); 856 scManager.addDynamicShortcuts(Arrays.asList(shortcut)); 857 } 858 deleteShortcuts()859 private void deleteShortcuts() { 860 ShortcutManager scManager = 861 (ShortcutManager) mContext.getSystemService(Context.SHORTCUT_SERVICE); 862 scManager.removeAllDynamicShortcuts(); 863 scManager.removeLongLivedShortcuts(Collections.singletonList(SHARE_SHORTCUT_ID)); 864 } 865 866 /** 867 * Notification fulfilling conversation policy; for the shortcut to be valid 868 * call {@link #createDynamicShortcut()} 869 */ getConversationNotification()870 private Notification.Builder getConversationNotification() { 871 Person person = new Person.Builder() 872 .setName("bubblebot") 873 .build(); 874 Notification.Builder nb = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 875 .setContentTitle("foo") 876 .setShortcutId(SHARE_SHORTCUT_ID) 877 .setStyle(new Notification.MessagingStyle(person) 878 .setConversationTitle("Bubble Chat") 879 .addMessage("Hello?", 880 SystemClock.currentThreadTimeMillis() - 300000, person) 881 .addMessage("Is it me you're looking for?", 882 SystemClock.currentThreadTimeMillis(), person) 883 ) 884 .setSmallIcon(android.R.drawable.sym_def_app_icon); 885 return nb; 886 } 887 888 /** 889 * Starts an activity that is able to send a bubble; also handles unlocking the device. 890 * Any tests that use this method should be sure to call {@link #cleanupSendBubbleActivity()} 891 * to unregister the related broadcast receiver. 892 * 893 * @return the SendBubbleActivity that was opened. 894 */ startSendBubbleActivity()895 private SendBubbleActivity startSendBubbleActivity() { 896 final CountDownLatch latch = new CountDownLatch(2); 897 mBubbleBroadcastReceiver = new BroadcastReceiver() { 898 @Override 899 public void onReceive(Context context, Intent intent) { 900 latch.countDown(); 901 } 902 }; 903 IntentFilter filter = new IntentFilter(SendBubbleActivity.BUBBLE_ACTIVITY_OPENED); 904 mContext.registerReceiver(mBubbleBroadcastReceiver, filter); 905 906 // Start & get the activity 907 Class clazz = SendBubbleActivity.class; 908 909 Instrumentation.ActivityResult result = 910 new Instrumentation.ActivityResult(0, new Intent()); 911 Instrumentation.ActivityMonitor monitor = 912 new Instrumentation.ActivityMonitor(clazz.getName(), result, false); 913 InstrumentationRegistry.getInstrumentation().addMonitor(monitor); 914 915 Intent i = new Intent(mContext, SendBubbleActivity.class); 916 i.setFlags(FLAG_ACTIVITY_NEW_TASK); 917 InstrumentationRegistry.getInstrumentation().startActivitySync(i); 918 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 919 920 SendBubbleActivity sendBubbleActivity = (SendBubbleActivity) monitor.waitForActivity(); 921 922 // Make sure device is unlocked 923 KeyguardManager keyguardManager = mContext.getSystemService(KeyguardManager.class); 924 keyguardManager.requestDismissKeyguard(sendBubbleActivity, 925 new KeyguardManager.KeyguardDismissCallback() { 926 @Override 927 public void onDismissSucceeded() { 928 latch.countDown(); 929 } 930 }); 931 try { 932 latch.await(500, TimeUnit.MILLISECONDS); 933 } catch (InterruptedException e) { 934 e.printStackTrace(); 935 } 936 return sendBubbleActivity; 937 } 938 cleanupSendBubbleActivity()939 private void cleanupSendBubbleActivity() { 940 mContext.unregisterReceiver(mBubbleBroadcastReceiver); 941 } 942 sendTrampolineMessage(ComponentName component, int message, int notificationId, Handler callback)943 private void sendTrampolineMessage(ComponentName component, int message, 944 int notificationId, Handler callback) throws Exception { 945 if (mTrampolineConnection == null) { 946 Intent intent = new Intent(); 947 intent.setComponent(component); 948 mTrampolineConnection = new FutureServiceConnection(); 949 assertTrue( 950 mContext.bindService(intent, mTrampolineConnection, Context.BIND_AUTO_CREATE)); 951 } 952 Messenger service = new Messenger(mTrampolineConnection.get(TIMEOUT_MS)); 953 service.send(Message.obtain(null, message, notificationId, -1, new Messenger(callback))); 954 } 955 setDefaultBrowser(String packageName)956 private void setDefaultBrowser(String packageName) throws Exception { 957 UserHandle user = android.os.Process.myUserHandle(); 958 mPreviousDefaultBrowser = SystemUtil.callWithShellPermissionIdentity( 959 () -> mRoleManager.getRoleHoldersAsUser(RoleManager.ROLE_BROWSER, user)); 960 CompletableFuture<Boolean> set = new CompletableFuture<>(); 961 SystemUtil.runWithShellPermissionIdentity( 962 () -> mRoleManager.addRoleHolderAsUser(RoleManager.ROLE_BROWSER, packageName, 0, 963 user, mContext.getMainExecutor(), set::complete)); 964 assertTrue("Failed to set " + packageName + " as default browser", 965 set.get(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 966 } 967 restoreDefaultBrowser()968 private void restoreDefaultBrowser() throws Exception { 969 Preconditions.checkState(mPreviousDefaultBrowser != null); 970 UserHandle user = android.os.Process.myUserHandle(); 971 Executor executor = mContext.getMainExecutor(); 972 CompletableFuture<Boolean> restored = new CompletableFuture<>(); 973 SystemUtil.runWithShellPermissionIdentity(() -> { 974 mRoleManager.clearRoleHoldersAsUser(RoleManager.ROLE_BROWSER, 0, user, executor, 975 restored::complete); 976 for (String packageName : mPreviousDefaultBrowser) { 977 mRoleManager.addRoleHolderAsUser(RoleManager.ROLE_BROWSER, packageName, 978 0, user, executor, restored::complete); 979 } 980 }); 981 assertTrue("Failed to restore default browser", 982 restored.get(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 983 } 984 985 /** 986 * Previous tests could have started activities within the grace period, so go home to avoid 987 * allowing background activity starts due to this exemption. 988 */ deactivateGracePeriod()989 private void deactivateGracePeriod() { 990 UiDevice.getInstance(mInstrumentation).pressHome(); 991 } 992 testConsolidatedNotificationPolicy()993 public void testConsolidatedNotificationPolicy() throws Exception { 994 final int originalFilter = mNotificationManager.getCurrentInterruptionFilter(); 995 Policy origPolicy = mNotificationManager.getNotificationPolicy(); 996 try { 997 toggleNotificationPolicyAccess(mContext.getPackageName(), 998 InstrumentationRegistry.getInstrumentation(), true); 999 1000 mNotificationManager.setNotificationPolicy(new NotificationManager.Policy( 1001 PRIORITY_CATEGORY_ALARMS | PRIORITY_CATEGORY_MEDIA, 1002 0, 0)); 1003 // turn on manual DND 1004 mNotificationManager.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY); 1005 assertExpectedDndState(INTERRUPTION_FILTER_PRIORITY); 1006 1007 // no custom ZenPolicy, so consolidatedPolicy should equal the default notif policy 1008 assertEquals(mNotificationManager.getConsolidatedNotificationPolicy(), 1009 mNotificationManager.getNotificationPolicy()); 1010 1011 // turn off manual DND 1012 mNotificationManager.setInterruptionFilter(INTERRUPTION_FILTER_ALL); 1013 assertExpectedDndState(INTERRUPTION_FILTER_ALL); 1014 1015 // setup custom ZenPolicy for an automatic rule 1016 AutomaticZenRule rule = createRule("test_consolidated_policy", 1017 INTERRUPTION_FILTER_PRIORITY); 1018 rule.setZenPolicy(new ZenPolicy.Builder() 1019 .allowReminders(true) 1020 .build()); 1021 String id = mNotificationManager.addAutomaticZenRule(rule); 1022 mRuleIds.add(id); 1023 // set condition of the automatic rule to TRUE 1024 Condition condition = new Condition(rule.getConditionId(), "summary", 1025 Condition.STATE_TRUE); 1026 mNotificationManager.setAutomaticZenRuleState(id, condition); 1027 assertExpectedDndState(INTERRUPTION_FILTER_PRIORITY); 1028 1029 NotificationManager.Policy consolidatedPolicy = 1030 mNotificationManager.getConsolidatedNotificationPolicy(); 1031 1032 // alarms and media are allowed from default notification policy 1033 assertTrue((consolidatedPolicy.priorityCategories & PRIORITY_CATEGORY_ALARMS) != 0); 1034 assertTrue((consolidatedPolicy.priorityCategories & PRIORITY_CATEGORY_MEDIA) != 0); 1035 1036 // reminders is allowed from the automatic rule's custom ZenPolicy 1037 assertTrue((consolidatedPolicy.priorityCategories & PRIORITY_CATEGORY_REMINDERS) != 0); 1038 1039 // other sounds aren't allowed 1040 assertTrue((consolidatedPolicy.priorityCategories 1041 & PRIORITY_CATEGORY_CONVERSATIONS) == 0); 1042 assertTrue((consolidatedPolicy.priorityCategories & PRIORITY_CATEGORY_CALLS) == 0); 1043 assertTrue((consolidatedPolicy.priorityCategories & PRIORITY_CATEGORY_MESSAGES) == 0); 1044 assertTrue((consolidatedPolicy.priorityCategories & PRIORITY_CATEGORY_SYSTEM) == 0); 1045 assertTrue((consolidatedPolicy.priorityCategories & PRIORITY_CATEGORY_EVENTS) == 0); 1046 } finally { 1047 mNotificationManager.setInterruptionFilter(originalFilter); 1048 mNotificationManager.setNotificationPolicy(origPolicy); 1049 } 1050 } 1051 testConsolidatedNotificationPolicyMultiRules()1052 public void testConsolidatedNotificationPolicyMultiRules() throws Exception { 1053 final int originalFilter = mNotificationManager.getCurrentInterruptionFilter(); 1054 Policy origPolicy = mNotificationManager.getNotificationPolicy(); 1055 try { 1056 toggleNotificationPolicyAccess(mContext.getPackageName(), 1057 InstrumentationRegistry.getInstrumentation(), true); 1058 1059 // default allows no sounds 1060 mNotificationManager.setNotificationPolicy(new NotificationManager.Policy( 1061 PRIORITY_CATEGORY_ALARMS, 0, 0)); 1062 1063 // setup custom ZenPolicy for two automatic rules 1064 AutomaticZenRule rule1 = createRule("test_consolidated_policyq", 1065 INTERRUPTION_FILTER_PRIORITY); 1066 rule1.setZenPolicy(new ZenPolicy.Builder() 1067 .allowReminders(false) 1068 .allowAlarms(false) 1069 .allowSystem(true) 1070 .build()); 1071 AutomaticZenRule rule2 = createRule("test_consolidated_policy2", 1072 INTERRUPTION_FILTER_PRIORITY); 1073 rule2.setZenPolicy(new ZenPolicy.Builder() 1074 .allowReminders(true) 1075 .allowMedia(true) 1076 .build()); 1077 String id1 = mNotificationManager.addAutomaticZenRule(rule1); 1078 String id2 = mNotificationManager.addAutomaticZenRule(rule2); 1079 Condition onCondition1 = new Condition(rule1.getConditionId(), "summary", 1080 Condition.STATE_TRUE); 1081 Condition onCondition2 = new Condition(rule2.getConditionId(), "summary", 1082 Condition.STATE_TRUE); 1083 mNotificationManager.setAutomaticZenRuleState(id1, onCondition1); 1084 mNotificationManager.setAutomaticZenRuleState(id2, onCondition2); 1085 1086 Thread.sleep(300); // wait for rules to be applied - it's done asynchronously 1087 1088 mRuleIds.add(id1); 1089 mRuleIds.add(id2); 1090 assertExpectedDndState(INTERRUPTION_FILTER_PRIORITY); 1091 1092 NotificationManager.Policy consolidatedPolicy = 1093 mNotificationManager.getConsolidatedNotificationPolicy(); 1094 1095 // reminders aren't allowed from rule1 overriding rule2 1096 // (not allowed takes precedence over allowed) 1097 assertTrue((consolidatedPolicy.priorityCategories & PRIORITY_CATEGORY_REMINDERS) == 0); 1098 1099 // alarms aren't allowed from rule1 1100 // (rule's custom zenPolicy overrides default policy) 1101 assertTrue((consolidatedPolicy.priorityCategories & PRIORITY_CATEGORY_ALARMS) == 0); 1102 1103 // system is allowed from rule1, media is allowed from rule2 1104 assertTrue((consolidatedPolicy.priorityCategories & PRIORITY_CATEGORY_SYSTEM) != 0); 1105 assertTrue((consolidatedPolicy.priorityCategories & PRIORITY_CATEGORY_MEDIA) != 0); 1106 1107 // other sounds aren't allowed (from default policy) 1108 assertTrue((consolidatedPolicy.priorityCategories 1109 & PRIORITY_CATEGORY_CONVERSATIONS) == 0); 1110 assertTrue((consolidatedPolicy.priorityCategories & PRIORITY_CATEGORY_CALLS) == 0); 1111 assertTrue((consolidatedPolicy.priorityCategories & PRIORITY_CATEGORY_MESSAGES) == 0); 1112 assertTrue((consolidatedPolicy.priorityCategories & PRIORITY_CATEGORY_EVENTS) == 0); 1113 } finally { 1114 mNotificationManager.setInterruptionFilter(originalFilter); 1115 mNotificationManager.setNotificationPolicy(origPolicy); 1116 } 1117 } 1118 testPostPCanToggleAlarmsMediaSystemTest()1119 public void testPostPCanToggleAlarmsMediaSystemTest() throws Exception { 1120 toggleNotificationPolicyAccess(mContext.getPackageName(), 1121 InstrumentationRegistry.getInstrumentation(), true); 1122 1123 NotificationManager.Policy origPolicy = mNotificationManager.getNotificationPolicy(); 1124 try { 1125 if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.P) { 1126 // Post-P can toggle alarms, media, system 1127 // toggle on alarms, media, system: 1128 mNotificationManager.setNotificationPolicy(new NotificationManager.Policy( 1129 PRIORITY_CATEGORY_ALARMS 1130 | PRIORITY_CATEGORY_MEDIA 1131 | PRIORITY_CATEGORY_SYSTEM, 0, 0)); 1132 NotificationManager.Policy policy = mNotificationManager.getNotificationPolicy(); 1133 assertTrue((policy.priorityCategories & PRIORITY_CATEGORY_ALARMS) != 0); 1134 assertTrue((policy.priorityCategories & PRIORITY_CATEGORY_MEDIA) != 0); 1135 assertTrue((policy.priorityCategories & PRIORITY_CATEGORY_SYSTEM) != 0); 1136 1137 // toggle off alarms, media, system 1138 mNotificationManager.setNotificationPolicy(new NotificationManager.Policy(0, 0, 0)); 1139 policy = mNotificationManager.getNotificationPolicy(); 1140 assertTrue((policy.priorityCategories & PRIORITY_CATEGORY_ALARMS) == 0); 1141 assertTrue((policy.priorityCategories & PRIORITY_CATEGORY_MEDIA) == 0); 1142 assertTrue((policy.priorityCategories & PRIORITY_CATEGORY_SYSTEM) == 0); 1143 } 1144 } finally { 1145 mNotificationManager.setNotificationPolicy(origPolicy); 1146 } 1147 } 1148 testPostRCanToggleConversationsTest()1149 public void testPostRCanToggleConversationsTest() throws Exception { 1150 toggleNotificationPolicyAccess(mContext.getPackageName(), 1151 InstrumentationRegistry.getInstrumentation(), true); 1152 1153 NotificationManager.Policy origPolicy = mNotificationManager.getNotificationPolicy(); 1154 1155 try { 1156 mNotificationManager.setNotificationPolicy(new NotificationManager.Policy( 1157 0, 0, 0, 0)); 1158 NotificationManager.Policy policy = mNotificationManager.getNotificationPolicy(); 1159 assertEquals(0, (policy.priorityCategories & PRIORITY_CATEGORY_CONVERSATIONS)); 1160 assertEquals(CONVERSATION_SENDERS_NONE, policy.priorityConversationSenders); 1161 1162 mNotificationManager.setNotificationPolicy(new NotificationManager.Policy( 1163 PRIORITY_CATEGORY_CONVERSATIONS, 0, 0, 0, CONVERSATION_SENDERS_ANYONE)); 1164 policy = mNotificationManager.getNotificationPolicy(); 1165 assertTrue((policy.priorityCategories & PRIORITY_CATEGORY_CONVERSATIONS) != 0); 1166 assertEquals(CONVERSATION_SENDERS_ANYONE, policy.priorityConversationSenders); 1167 1168 } finally { 1169 mNotificationManager.setNotificationPolicy(origPolicy); 1170 } 1171 } 1172 testCreateChannelGroup()1173 public void testCreateChannelGroup() throws Exception { 1174 final NotificationChannelGroup ncg = new NotificationChannelGroup("a group", "a label"); 1175 final NotificationChannel channel = 1176 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 1177 channel.setGroup(ncg.getId()); 1178 mNotificationManager.createNotificationChannelGroup(ncg); 1179 final NotificationChannel ungrouped = 1180 new NotificationChannel(mId + "!", "name", IMPORTANCE_DEFAULT); 1181 try { 1182 mNotificationManager.createNotificationChannel(channel); 1183 mNotificationManager.createNotificationChannel(ungrouped); 1184 1185 List<NotificationChannelGroup> ncgs = 1186 mNotificationManager.getNotificationChannelGroups(); 1187 assertEquals(1, ncgs.size()); 1188 assertEquals(ncg.getName(), ncgs.get(0).getName()); 1189 assertEquals(ncg.getDescription(), ncgs.get(0).getDescription()); 1190 assertEquals(channel.getId(), ncgs.get(0).getChannels().get(0).getId()); 1191 } finally { 1192 mNotificationManager.deleteNotificationChannelGroup(ncg.getId()); 1193 } 1194 } 1195 testGetChannelGroup()1196 public void testGetChannelGroup() throws Exception { 1197 final NotificationChannelGroup ncg = new NotificationChannelGroup("a group", "a label"); 1198 ncg.setDescription("bananas"); 1199 final NotificationChannelGroup ncg2 = new NotificationChannelGroup("group 2", "label 2"); 1200 final NotificationChannel channel = 1201 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 1202 channel.setGroup(ncg.getId()); 1203 1204 mNotificationManager.createNotificationChannelGroup(ncg); 1205 mNotificationManager.createNotificationChannelGroup(ncg2); 1206 mNotificationManager.createNotificationChannel(channel); 1207 1208 NotificationChannelGroup actual = 1209 mNotificationManager.getNotificationChannelGroup(ncg.getId()); 1210 assertEquals(ncg.getId(), actual.getId()); 1211 assertEquals(ncg.getName(), actual.getName()); 1212 assertEquals(ncg.getDescription(), actual.getDescription()); 1213 assertEquals(channel.getId(), actual.getChannels().get(0).getId()); 1214 } 1215 testGetChannelGroups()1216 public void testGetChannelGroups() throws Exception { 1217 final NotificationChannelGroup ncg = new NotificationChannelGroup("a group", "a label"); 1218 ncg.setDescription("bananas"); 1219 final NotificationChannelGroup ncg2 = new NotificationChannelGroup("group 2", "label 2"); 1220 final NotificationChannel channel = 1221 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 1222 channel.setGroup(ncg2.getId()); 1223 1224 mNotificationManager.createNotificationChannelGroup(ncg); 1225 mNotificationManager.createNotificationChannelGroup(ncg2); 1226 mNotificationManager.createNotificationChannel(channel); 1227 1228 List<NotificationChannelGroup> actual = 1229 mNotificationManager.getNotificationChannelGroups(); 1230 assertEquals(2, actual.size()); 1231 for (NotificationChannelGroup group : actual) { 1232 if (group.getId().equals(ncg.getId())) { 1233 assertEquals(group.getName(), ncg.getName()); 1234 assertEquals(group.getDescription(), ncg.getDescription()); 1235 assertEquals(0, group.getChannels().size()); 1236 } else if (group.getId().equals(ncg2.getId())) { 1237 assertEquals(group.getName(), ncg2.getName()); 1238 assertEquals(group.getDescription(), ncg2.getDescription()); 1239 assertEquals(1, group.getChannels().size()); 1240 assertEquals(channel.getId(), group.getChannels().get(0).getId()); 1241 } else { 1242 fail("Extra group found " + group.getId()); 1243 } 1244 } 1245 } 1246 testDeleteChannelGroup()1247 public void testDeleteChannelGroup() throws Exception { 1248 final NotificationChannelGroup ncg = new NotificationChannelGroup("a group", "a label"); 1249 final NotificationChannel channel = 1250 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 1251 channel.setGroup(ncg.getId()); 1252 mNotificationManager.createNotificationChannelGroup(ncg); 1253 mNotificationManager.createNotificationChannel(channel); 1254 1255 mNotificationManager.deleteNotificationChannelGroup(ncg.getId()); 1256 1257 assertNull(mNotificationManager.getNotificationChannel(channel.getId())); 1258 assertEquals(0, mNotificationManager.getNotificationChannelGroups().size()); 1259 } 1260 testCreateChannel()1261 public void testCreateChannel() throws Exception { 1262 final NotificationChannel channel = 1263 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 1264 channel.setDescription("bananas"); 1265 channel.enableVibration(true); 1266 channel.setVibrationPattern(new long[]{5, 8, 2, 1}); 1267 channel.setSound(new Uri.Builder().scheme("test").build(), 1268 new AudioAttributes.Builder().setUsage( 1269 AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED).build()); 1270 channel.enableLights(true); 1271 channel.setBypassDnd(true); 1272 channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET); 1273 mNotificationManager.createNotificationChannel(channel); 1274 final NotificationChannel createdChannel = 1275 mNotificationManager.getNotificationChannel(mId); 1276 compareChannels(channel, createdChannel); 1277 // Lockscreen Visibility and canBypassDnd no longer settable. 1278 assertTrue(createdChannel.getLockscreenVisibility() != Notification.VISIBILITY_SECRET); 1279 assertFalse(createdChannel.canBypassDnd()); 1280 } 1281 testCreateChannel_rename()1282 public void testCreateChannel_rename() throws Exception { 1283 NotificationChannel channel = 1284 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 1285 mNotificationManager.createNotificationChannel(channel); 1286 channel.setName("new name"); 1287 mNotificationManager.createNotificationChannel(channel); 1288 final NotificationChannel createdChannel = 1289 mNotificationManager.getNotificationChannel(mId); 1290 compareChannels(channel, createdChannel); 1291 1292 channel.setImportance(NotificationManager.IMPORTANCE_HIGH); 1293 mNotificationManager.createNotificationChannel(channel); 1294 assertEquals(NotificationManager.IMPORTANCE_DEFAULT, 1295 mNotificationManager.getNotificationChannel(mId).getImportance()); 1296 } 1297 testCreateChannel_addToGroup()1298 public void testCreateChannel_addToGroup() throws Exception { 1299 String oldGroup = null; 1300 String newGroup = "new group"; 1301 mNotificationManager.createNotificationChannelGroup( 1302 new NotificationChannelGroup(newGroup, newGroup)); 1303 1304 NotificationChannel channel = 1305 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 1306 channel.setGroup(oldGroup); 1307 mNotificationManager.createNotificationChannel(channel); 1308 1309 channel.setGroup(newGroup); 1310 mNotificationManager.createNotificationChannel(channel); 1311 1312 final NotificationChannel updatedChannel = 1313 mNotificationManager.getNotificationChannel(mId); 1314 assertEquals("Failed to add non-grouped channel to a group on update ", 1315 newGroup, updatedChannel.getGroup()); 1316 } 1317 testCreateChannel_cannotChangeGroup()1318 public void testCreateChannel_cannotChangeGroup() throws Exception { 1319 String oldGroup = "old group"; 1320 String newGroup = "new group"; 1321 mNotificationManager.createNotificationChannelGroup( 1322 new NotificationChannelGroup(oldGroup, oldGroup)); 1323 mNotificationManager.createNotificationChannelGroup( 1324 new NotificationChannelGroup(newGroup, newGroup)); 1325 1326 NotificationChannel channel = 1327 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 1328 channel.setGroup(oldGroup); 1329 mNotificationManager.createNotificationChannel(channel); 1330 channel.setGroup(newGroup); 1331 mNotificationManager.createNotificationChannel(channel); 1332 final NotificationChannel updatedChannel = 1333 mNotificationManager.getNotificationChannel(mId); 1334 assertEquals("Channels should not be allowed to change groups", 1335 oldGroup, updatedChannel.getGroup()); 1336 } 1337 testCreateSameChannelDoesNotUpdate()1338 public void testCreateSameChannelDoesNotUpdate() throws Exception { 1339 final NotificationChannel channel = 1340 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 1341 mNotificationManager.createNotificationChannel(channel); 1342 final NotificationChannel channelDupe = 1343 new NotificationChannel(mId, "name", IMPORTANCE_HIGH); 1344 mNotificationManager.createNotificationChannel(channelDupe); 1345 final NotificationChannel createdChannel = 1346 mNotificationManager.getNotificationChannel(mId); 1347 compareChannels(channel, createdChannel); 1348 } 1349 testCreateChannelAlreadyExistsNoOp()1350 public void testCreateChannelAlreadyExistsNoOp() throws Exception { 1351 NotificationChannel channel = 1352 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 1353 mNotificationManager.createNotificationChannel(channel); 1354 NotificationChannel channelDupe = 1355 new NotificationChannel(mId, "name", IMPORTANCE_HIGH); 1356 mNotificationManager.createNotificationChannel(channelDupe); 1357 compareChannels(channel, mNotificationManager.getNotificationChannel(channel.getId())); 1358 } 1359 testCreateChannelWithGroup()1360 public void testCreateChannelWithGroup() throws Exception { 1361 NotificationChannelGroup ncg = new NotificationChannelGroup("g", "n"); 1362 mNotificationManager.createNotificationChannelGroup(ncg); 1363 try { 1364 NotificationChannel channel = 1365 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 1366 channel.setGroup(ncg.getId()); 1367 mNotificationManager.createNotificationChannel(channel); 1368 compareChannels(channel, mNotificationManager.getNotificationChannel(channel.getId())); 1369 } finally { 1370 mNotificationManager.deleteNotificationChannelGroup(ncg.getId()); 1371 } 1372 } 1373 testCreateChannelWithBadGroup()1374 public void testCreateChannelWithBadGroup() throws Exception { 1375 NotificationChannel channel = 1376 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 1377 channel.setGroup("garbage"); 1378 try { 1379 mNotificationManager.createNotificationChannel(channel); 1380 fail("Created notification with bad group"); 1381 } catch (IllegalArgumentException e) { 1382 } 1383 } 1384 testCreateChannelInvalidImportance()1385 public void testCreateChannelInvalidImportance() throws Exception { 1386 NotificationChannel channel = 1387 new NotificationChannel(mId, "name", IMPORTANCE_UNSPECIFIED); 1388 try { 1389 mNotificationManager.createNotificationChannel(channel); 1390 } catch (IllegalArgumentException e) { 1391 //success 1392 } 1393 } 1394 testDeleteChannel()1395 public void testDeleteChannel() throws Exception { 1396 NotificationChannel channel = 1397 new NotificationChannel(mId, "name", IMPORTANCE_LOW); 1398 mNotificationManager.createNotificationChannel(channel); 1399 compareChannels(channel, mNotificationManager.getNotificationChannel(channel.getId())); 1400 mNotificationManager.deleteNotificationChannel(channel.getId()); 1401 assertNull(mNotificationManager.getNotificationChannel(channel.getId())); 1402 } 1403 testCannotDeleteDefaultChannel()1404 public void testCannotDeleteDefaultChannel() throws Exception { 1405 try { 1406 mNotificationManager.deleteNotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID); 1407 fail("Deleted default channel"); 1408 } catch (IllegalArgumentException e) { 1409 //success 1410 } 1411 } 1412 testGetChannel()1413 public void testGetChannel() throws Exception { 1414 NotificationChannel channel1 = 1415 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 1416 NotificationChannel channel2 = 1417 new NotificationChannel( 1418 UUID.randomUUID().toString(), "name2", IMPORTANCE_HIGH); 1419 NotificationChannel channel3 = 1420 new NotificationChannel( 1421 UUID.randomUUID().toString(), "name3", IMPORTANCE_LOW); 1422 NotificationChannel channel4 = 1423 new NotificationChannel( 1424 UUID.randomUUID().toString(), "name4", IMPORTANCE_MIN); 1425 mNotificationManager.createNotificationChannel(channel1); 1426 mNotificationManager.createNotificationChannel(channel2); 1427 mNotificationManager.createNotificationChannel(channel3); 1428 mNotificationManager.createNotificationChannel(channel4); 1429 1430 compareChannels(channel2, 1431 mNotificationManager.getNotificationChannel(channel2.getId())); 1432 compareChannels(channel3, 1433 mNotificationManager.getNotificationChannel(channel3.getId())); 1434 compareChannels(channel1, 1435 mNotificationManager.getNotificationChannel(channel1.getId())); 1436 compareChannels(channel4, 1437 mNotificationManager.getNotificationChannel(channel4.getId())); 1438 } 1439 testGetChannels()1440 public void testGetChannels() throws Exception { 1441 NotificationChannel channel1 = 1442 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 1443 NotificationChannel channel2 = 1444 new NotificationChannel( 1445 UUID.randomUUID().toString(), "name2", IMPORTANCE_HIGH); 1446 NotificationChannel channel3 = 1447 new NotificationChannel( 1448 UUID.randomUUID().toString(), "name3", IMPORTANCE_LOW); 1449 NotificationChannel channel4 = 1450 new NotificationChannel( 1451 UUID.randomUUID().toString(), "name4", IMPORTANCE_MIN); 1452 1453 Map<String, NotificationChannel> channelMap = new HashMap<>(); 1454 channelMap.put(channel1.getId(), channel1); 1455 channelMap.put(channel2.getId(), channel2); 1456 channelMap.put(channel3.getId(), channel3); 1457 channelMap.put(channel4.getId(), channel4); 1458 mNotificationManager.createNotificationChannel(channel1); 1459 mNotificationManager.createNotificationChannel(channel2); 1460 mNotificationManager.createNotificationChannel(channel3); 1461 mNotificationManager.createNotificationChannel(channel4); 1462 1463 mNotificationManager.deleteNotificationChannel(channel3.getId()); 1464 1465 List<NotificationChannel> channels = mNotificationManager.getNotificationChannels(); 1466 for (NotificationChannel nc : channels) { 1467 if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(nc.getId())) { 1468 continue; 1469 } 1470 if (NOTIFICATION_CHANNEL_ID.equals(nc.getId())) { 1471 continue; 1472 } 1473 assertFalse(channel3.getId().equals(nc.getId())); 1474 if (!channelMap.containsKey(nc.getId())) { 1475 // failed cleanup from prior test run; ignore 1476 continue; 1477 } 1478 compareChannels(channelMap.get(nc.getId()), nc); 1479 } 1480 } 1481 testRecreateDeletedChannel()1482 public void testRecreateDeletedChannel() throws Exception { 1483 NotificationChannel channel = 1484 new NotificationChannel(mId, "name", IMPORTANCE_DEFAULT); 1485 channel.setShowBadge(true); 1486 NotificationChannel newChannel = new NotificationChannel( 1487 channel.getId(), channel.getName(), IMPORTANCE_HIGH); 1488 mNotificationManager.createNotificationChannel(channel); 1489 mNotificationManager.deleteNotificationChannel(channel.getId()); 1490 1491 mNotificationManager.createNotificationChannel(newChannel); 1492 1493 compareChannels(channel, 1494 mNotificationManager.getNotificationChannel(newChannel.getId())); 1495 } 1496 testNotify()1497 public void testNotify() throws Exception { 1498 mNotificationManager.cancelAll(); 1499 1500 final int id = 1; 1501 sendNotification(id, R.drawable.black); 1502 // test updating the same notification 1503 sendNotification(id, R.drawable.blue); 1504 sendNotification(id, R.drawable.yellow); 1505 1506 // assume that sendNotification tested to make sure individual notifications were present 1507 StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications(); 1508 for (StatusBarNotification sbn : sbns) { 1509 if (sbn.getId() != id) { 1510 fail("we got back other notifications besides the one we posted: " 1511 + sbn.getKey()); 1512 } 1513 } 1514 } 1515 testSuspendPackage_withoutShellPermission()1516 public void testSuspendPackage_withoutShellPermission() throws Exception { 1517 if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) { 1518 return; 1519 } 1520 1521 try { 1522 Process proc = Runtime.getRuntime().exec("cmd notification suspend_package " 1523 + mContext.getPackageName()); 1524 1525 // read output of command 1526 BufferedReader reader = 1527 new BufferedReader(new InputStreamReader(proc.getInputStream())); 1528 StringBuilder output = new StringBuilder(); 1529 String line = reader.readLine(); 1530 while (line != null) { 1531 output.append(line); 1532 line = reader.readLine(); 1533 } 1534 reader.close(); 1535 final String outputString = output.toString(); 1536 1537 proc.waitFor(); 1538 1539 // check that the output string had an error / disallowed call since it didn't have 1540 // shell permission to suspend the package 1541 assertTrue(outputString, outputString.contains("error")); 1542 assertTrue(outputString, outputString.contains("permission denied")); 1543 } catch (InterruptedException e) { 1544 fail("Unsuccessful shell command"); 1545 } 1546 } 1547 testSuspendPackage()1548 public void testSuspendPackage() throws Exception { 1549 toggleListenerAccess(true); 1550 Thread.sleep(500); // wait for listener to be allowed 1551 1552 mListener = TestNotificationListener.getInstance(); 1553 assertNotNull(mListener); 1554 1555 sendNotification(1, R.drawable.black); 1556 Thread.sleep(500); // wait for notification listener to receive notification 1557 assertEquals(1, mListener.mPosted.size()); 1558 1559 // suspend package, ranking should be updated with suspended = true 1560 suspendPackage(mContext.getPackageName(), InstrumentationRegistry.getInstrumentation(), 1561 true); 1562 Thread.sleep(500); // wait for notification listener to get response 1563 NotificationListenerService.RankingMap rankingMap = mListener.mRankingMap; 1564 NotificationListenerService.Ranking outRanking = new NotificationListenerService.Ranking(); 1565 for (String key : rankingMap.getOrderedKeys()) { 1566 if (key.contains(mListener.getPackageName())) { 1567 rankingMap.getRanking(key, outRanking); 1568 Log.d(TAG, "key=" + key + " suspended=" + outRanking.isSuspended()); 1569 assertTrue(outRanking.isSuspended()); 1570 } 1571 } 1572 1573 // unsuspend package, ranking should be updated with suspended = false 1574 suspendPackage(mContext.getPackageName(), InstrumentationRegistry.getInstrumentation(), 1575 false); 1576 Thread.sleep(500); // wait for notification listener to get response 1577 rankingMap = mListener.mRankingMap; 1578 for (String key : rankingMap.getOrderedKeys()) { 1579 if (key.contains(mListener.getPackageName())) { 1580 rankingMap.getRanking(key, outRanking); 1581 Log.d(TAG, "key=" + key + " suspended=" + outRanking.isSuspended()); 1582 assertFalse(outRanking.isSuspended()); 1583 } 1584 } 1585 1586 mListener.resetData(); 1587 } 1588 testSuspendedPackageSendsNotification()1589 public void testSuspendedPackageSendsNotification() throws Exception { 1590 toggleListenerAccess(true); 1591 Thread.sleep(500); // wait for listener to be allowed 1592 1593 mListener = TestNotificationListener.getInstance(); 1594 assertNotNull(mListener); 1595 1596 // suspend package, post notification while package is suspended, see notification 1597 // in ranking map with suspended = true 1598 suspendPackage(mContext.getPackageName(), InstrumentationRegistry.getInstrumentation(), 1599 true); 1600 sendNotification(1, R.drawable.black); 1601 Thread.sleep(500); // wait for notification listener to receive notification 1602 assertEquals(1, mListener.mPosted.size()); // apps targeting P receive notification 1603 NotificationListenerService.RankingMap rankingMap = mListener.mRankingMap; 1604 NotificationListenerService.Ranking outRanking = new NotificationListenerService.Ranking(); 1605 for (String key : rankingMap.getOrderedKeys()) { 1606 if (key.contains(mListener.getPackageName())) { 1607 rankingMap.getRanking(key, outRanking); 1608 Log.d(TAG, "key=" + key + " suspended=" + outRanking.isSuspended()); 1609 assertTrue(outRanking.isSuspended()); 1610 } 1611 } 1612 1613 // unsuspend package, ranking should be updated with suspended = false 1614 suspendPackage(mContext.getPackageName(), InstrumentationRegistry.getInstrumentation(), 1615 false); 1616 Thread.sleep(500); // wait for notification listener to get response 1617 assertEquals(1, mListener.mPosted.size()); // should see previously posted notification 1618 rankingMap = mListener.mRankingMap; 1619 for (String key : rankingMap.getOrderedKeys()) { 1620 if (key.contains(mListener.getPackageName())) { 1621 rankingMap.getRanking(key, outRanking); 1622 Log.d(TAG, "key=" + key + " suspended=" + outRanking.isSuspended()); 1623 assertFalse(outRanking.isSuspended()); 1624 } 1625 } 1626 1627 mListener.resetData(); 1628 } 1629 testCanBubble_ranking()1630 public void testCanBubble_ranking() throws Exception { 1631 if ((mActivityManager.isLowRamDevice() && !FeatureUtil.isWatch()) 1632 || FeatureUtil.isAutomotive() || FeatureUtil.isTV()) { 1633 return; 1634 } 1635 1636 // turn on bubbles globally 1637 setBubblesGlobal(true); 1638 1639 assertEquals(1, Settings.Secure.getInt( 1640 mContext.getContentResolver(), Settings.Secure.NOTIFICATION_BUBBLES)); 1641 1642 toggleListenerAccess(true); 1643 Thread.sleep(500); // wait for listener to be allowed 1644 1645 mListener = TestNotificationListener.getInstance(); 1646 assertNotNull(mListener); 1647 1648 sendNotification(1, R.drawable.black); 1649 Thread.sleep(500); // wait for notification listener to receive notification 1650 NotificationListenerService.RankingMap rankingMap = mListener.mRankingMap; 1651 NotificationListenerService.Ranking outRanking = 1652 new NotificationListenerService.Ranking(); 1653 for (String key : rankingMap.getOrderedKeys()) { 1654 if (key.contains(mListener.getPackageName())) { 1655 rankingMap.getRanking(key, outRanking); 1656 // by default nothing can bubble 1657 assertFalse(outRanking.canBubble()); 1658 } 1659 } 1660 1661 // turn off bubbles globally 1662 setBubblesGlobal(false); 1663 1664 rankingMap = mListener.mRankingMap; 1665 outRanking = new NotificationListenerService.Ranking(); 1666 for (String key : rankingMap.getOrderedKeys()) { 1667 if (key.contains(mListener.getPackageName())) { 1668 rankingMap.getRanking(key, outRanking); 1669 assertFalse(outRanking.canBubble()); 1670 } 1671 } 1672 1673 mListener.resetData(); 1674 } 1675 testShowBadging_ranking()1676 public void testShowBadging_ranking() throws Exception { 1677 final int originalBadging = Settings.Secure.getInt( 1678 mContext.getContentResolver(), Settings.Secure.NOTIFICATION_BADGING); 1679 1680 SystemUtil.runWithShellPermissionIdentity(() -> 1681 Settings.Secure.putInt(mContext.getContentResolver(), 1682 Settings.Secure.NOTIFICATION_BADGING, 1)); 1683 assertEquals(1, Settings.Secure.getInt( 1684 mContext.getContentResolver(), Settings.Secure.NOTIFICATION_BADGING)); 1685 1686 toggleListenerAccess(true); 1687 Thread.sleep(500); // wait for listener to be allowed 1688 1689 mListener = TestNotificationListener.getInstance(); 1690 assertNotNull(mListener); 1691 try { 1692 sendNotification(1, R.drawable.black); 1693 Thread.sleep(500); // wait for notification listener to receive notification 1694 NotificationListenerService.RankingMap rankingMap = mListener.mRankingMap; 1695 NotificationListenerService.Ranking outRanking = 1696 new NotificationListenerService.Ranking(); 1697 for (String key : rankingMap.getOrderedKeys()) { 1698 if (key.contains(mListener.getPackageName())) { 1699 rankingMap.getRanking(key, outRanking); 1700 assertTrue(outRanking.canShowBadge()); 1701 } 1702 } 1703 1704 // turn off badging globally 1705 SystemUtil.runWithShellPermissionIdentity(() -> 1706 Settings.Secure.putInt(mContext.getContentResolver(), 1707 Settings.Secure.NOTIFICATION_BADGING, 0)); 1708 1709 Thread.sleep(500); // wait for ranking update 1710 1711 rankingMap = mListener.mRankingMap; 1712 outRanking = new NotificationListenerService.Ranking(); 1713 for (String key : rankingMap.getOrderedKeys()) { 1714 if (key.contains(mListener.getPackageName())) { 1715 assertFalse(outRanking.canShowBadge()); 1716 } 1717 } 1718 1719 mListener.resetData(); 1720 } finally { 1721 SystemUtil.runWithShellPermissionIdentity(() -> 1722 Settings.Secure.putInt(mContext.getContentResolver(), 1723 Settings.Secure.NOTIFICATION_BADGING, originalBadging)); 1724 } 1725 } 1726 testGetSuppressedVisualEffectsOff_ranking()1727 public void testGetSuppressedVisualEffectsOff_ranking() throws Exception { 1728 toggleListenerAccess(true); 1729 Thread.sleep(500); // wait for listener to be allowed 1730 1731 mListener = TestNotificationListener.getInstance(); 1732 assertNotNull(mListener); 1733 1734 final int notificationId = 1; 1735 sendNotification(notificationId, R.drawable.black); 1736 Thread.sleep(500); // wait for notification listener to receive notification 1737 1738 NotificationListenerService.RankingMap rankingMap = mListener.mRankingMap; 1739 NotificationListenerService.Ranking outRanking = 1740 new NotificationListenerService.Ranking(); 1741 1742 for (String key : rankingMap.getOrderedKeys()) { 1743 if (key.contains(mListener.getPackageName())) { 1744 rankingMap.getRanking(key, outRanking); 1745 1746 // check notification key match 1747 assertEquals(0, outRanking.getSuppressedVisualEffects()); 1748 } 1749 } 1750 } 1751 testGetSuppressedVisualEffects_ranking()1752 public void testGetSuppressedVisualEffects_ranking() throws Exception { 1753 final int originalFilter = mNotificationManager.getCurrentInterruptionFilter(); 1754 NotificationManager.Policy origPolicy = mNotificationManager.getNotificationPolicy(); 1755 try { 1756 toggleListenerAccess(true); 1757 Thread.sleep(500); // wait for listener to be allowed 1758 1759 mListener = TestNotificationListener.getInstance(); 1760 assertNotNull(mListener); 1761 1762 toggleNotificationPolicyAccess(mContext.getPackageName(), 1763 InstrumentationRegistry.getInstrumentation(), true); 1764 if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.P) { 1765 mNotificationManager.setNotificationPolicy(new NotificationManager.Policy(0, 0, 0, 1766 SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_PEEK)); 1767 } else { 1768 mNotificationManager.setNotificationPolicy(new NotificationManager.Policy(0, 0, 0, 1769 SUPPRESSED_EFFECT_SCREEN_ON)); 1770 } 1771 mNotificationManager.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY); 1772 1773 final int notificationId = 1; 1774 // update notification 1775 sendNotification(notificationId, R.drawable.black); 1776 Thread.sleep(500); // wait for notification listener to receive notification 1777 1778 NotificationListenerService.RankingMap rankingMap = mListener.mRankingMap; 1779 NotificationListenerService.Ranking outRanking = 1780 new NotificationListenerService.Ranking(); 1781 1782 for (String key : rankingMap.getOrderedKeys()) { 1783 if (key.contains(mListener.getPackageName())) { 1784 rankingMap.getRanking(key, outRanking); 1785 1786 if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.P) { 1787 assertEquals(SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_PEEK, 1788 outRanking.getSuppressedVisualEffects()); 1789 } else { 1790 assertEquals(SUPPRESSED_EFFECT_SCREEN_ON, 1791 outRanking.getSuppressedVisualEffects()); 1792 } 1793 } 1794 } 1795 } finally { 1796 // reset notification policy 1797 mNotificationManager.setInterruptionFilter(originalFilter); 1798 mNotificationManager.setNotificationPolicy(origPolicy); 1799 } 1800 1801 } 1802 testKeyChannelGroupOverrideImportanceExplanation_ranking()1803 public void testKeyChannelGroupOverrideImportanceExplanation_ranking() throws Exception { 1804 toggleListenerAccess(true); 1805 Thread.sleep(500); // wait for listener to be allowed 1806 1807 mListener = TestNotificationListener.getInstance(); 1808 assertNotNull(mListener); 1809 1810 final int notificationId = 1; 1811 sendNotification(notificationId, R.drawable.black); 1812 Thread.sleep(500); // wait for notification listener to receive notification 1813 1814 NotificationListenerService.RankingMap rankingMap = mListener.mRankingMap; 1815 NotificationListenerService.Ranking outRanking = 1816 new NotificationListenerService.Ranking(); 1817 1818 StatusBarNotification sbn = findPostedNotification(notificationId, false); 1819 1820 // check that the key and channel ids are the same in the ranking as the posted notification 1821 for (String key : rankingMap.getOrderedKeys()) { 1822 if (key.contains(mListener.getPackageName())) { 1823 rankingMap.getRanking(key, outRanking); 1824 1825 // check notification key match 1826 assertEquals(sbn.getKey(), outRanking.getKey()); 1827 1828 // check notification channel ids match 1829 assertEquals(sbn.getNotification().getChannelId(), outRanking.getChannel().getId()); 1830 1831 // check override group key match 1832 assertEquals(sbn.getOverrideGroupKey(), outRanking.getOverrideGroupKey()); 1833 1834 // check importance explanation isn't null 1835 assertNotNull(outRanking.getImportanceExplanation()); 1836 } 1837 } 1838 } 1839 testNotify_blockedChannel()1840 public void testNotify_blockedChannel() throws Exception { 1841 mNotificationManager.cancelAll(); 1842 1843 NotificationChannel channel = 1844 new NotificationChannel(mId, "name", IMPORTANCE_NONE); 1845 mNotificationManager.createNotificationChannel(channel); 1846 1847 int id = 1; 1848 final Notification notification = 1849 new Notification.Builder(mContext, mId) 1850 .setSmallIcon(R.drawable.black) 1851 .setWhen(System.currentTimeMillis()) 1852 .setContentTitle("notify#" + id) 1853 .setContentText("This is #" + id + "notification ") 1854 .build(); 1855 mNotificationManager.notify(id, notification); 1856 1857 if (!checkNotificationExistence(id, /*shouldExist=*/ false)) { 1858 fail("found unexpected notification id=" + id); 1859 } 1860 } 1861 testCancel()1862 public void testCancel() throws Exception { 1863 final int id = 9; 1864 sendNotification(id, R.drawable.black); 1865 // Wait for the notification posted not just enqueued 1866 try { 1867 Thread.sleep(500); 1868 } catch(InterruptedException e) {} 1869 mNotificationManager.cancel(id); 1870 1871 if (!checkNotificationExistence(id, /*shouldExist=*/ false)) { 1872 fail("canceled notification was still alive, id=" + id); 1873 } 1874 } 1875 testCancelAll()1876 public void testCancelAll() throws Exception { 1877 sendNotification(1, R.drawable.black); 1878 sendNotification(2, R.drawable.blue); 1879 sendNotification(3, R.drawable.yellow); 1880 1881 if (DEBUG) { 1882 Log.d(TAG, "posted 3 notifications, here they are: "); 1883 StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications(); 1884 for (StatusBarNotification sbn : sbns) { 1885 Log.d(TAG, " " + sbn); 1886 } 1887 Log.d(TAG, "about to cancel..."); 1888 } 1889 mNotificationManager.cancelAll(); 1890 1891 for (int id = 1; id <= 3; id++) { 1892 if (!checkNotificationExistence(id, /*shouldExist=*/ false)) { 1893 fail("Failed to cancel notification id=" + id); 1894 } 1895 } 1896 1897 } 1898 testNotifyWithTimeout()1899 public void testNotifyWithTimeout() throws Exception { 1900 mNotificationManager.cancelAll(); 1901 final int id = 128; 1902 final long timeout = 1000; 1903 1904 final Notification notification = 1905 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 1906 .setSmallIcon(R.drawable.black) 1907 .setContentTitle("notify#" + id) 1908 .setContentText("This is #" + id + "notification ") 1909 .setTimeoutAfter(timeout) 1910 .build(); 1911 mNotificationManager.notify(id, notification); 1912 1913 if (!checkNotificationExistence(id, /*shouldExist=*/ true)) { 1914 fail("couldn't find posted notification id=" + id); 1915 } 1916 1917 try { 1918 Thread.sleep(timeout); 1919 } catch (InterruptedException ex) { 1920 // pass 1921 } 1922 checkNotificationExistence(id, false); 1923 } 1924 testStyle()1925 public void testStyle() throws Exception { 1926 Notification.Style style = new Notification.Style() { 1927 public boolean areNotificationsVisiblyDifferent(Notification.Style other) { 1928 return false; 1929 } 1930 }; 1931 1932 Notification.Builder builder = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID); 1933 style.setBuilder(builder); 1934 1935 Notification notification = null; 1936 try { 1937 notification = style.build(); 1938 } catch (IllegalArgumentException e) { 1939 fail(e.getMessage()); 1940 } 1941 1942 assertNotNull(notification); 1943 1944 Notification builderNotification = builder.build(); 1945 assertEquals(builderNotification, notification); 1946 } 1947 testStyle_getStandardView()1948 public void testStyle_getStandardView() throws Exception { 1949 Notification.Builder builder = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID); 1950 int layoutId = 0; 1951 1952 TestStyle overrideStyle = new TestStyle(); 1953 overrideStyle.setBuilder(builder); 1954 RemoteViews result = overrideStyle.testGetStandardView(layoutId); 1955 1956 assertNotNull(result); 1957 assertEquals(layoutId, result.getLayoutId()); 1958 } 1959 1960 private class TestStyle extends Notification.Style { areNotificationsVisiblyDifferent(Notification.Style other)1961 public boolean areNotificationsVisiblyDifferent(Notification.Style other) { 1962 return false; 1963 } 1964 testGetStandardView(int layoutId)1965 public RemoteViews testGetStandardView(int layoutId) { 1966 // Wrapper method, since getStandardView is protected and otherwise unused in Android 1967 return getStandardView(layoutId); 1968 } 1969 } 1970 testMediaStyle_empty()1971 public void testMediaStyle_empty() { 1972 Notification.MediaStyle style = new Notification.MediaStyle(); 1973 assertNotNull(style); 1974 } 1975 testMediaStyle()1976 public void testMediaStyle() { 1977 mNotificationManager.cancelAll(); 1978 final int id = 99; 1979 MediaSession session = new MediaSession(getContext(), "media"); 1980 1981 final Notification notification = 1982 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 1983 .setSmallIcon(R.drawable.black) 1984 .setContentTitle("notify#" + id) 1985 .setContentText("This is #" + id + "notification ") 1986 .addAction(new Notification.Action.Builder( 1987 Icon.createWithResource(getContext(), R.drawable.icon_black), 1988 "play", getPendingIntent()).build()) 1989 .addAction(new Notification.Action.Builder( 1990 Icon.createWithResource(getContext(), R.drawable.icon_blue), 1991 "pause", getPendingIntent()).build()) 1992 .setStyle(new Notification.MediaStyle() 1993 .setShowActionsInCompactView(0, 1) 1994 .setMediaSession(session.getSessionToken())) 1995 .build(); 1996 mNotificationManager.notify(id, notification); 1997 1998 if (!checkNotificationExistence(id, /*shouldExist=*/ true)) { 1999 fail("couldn't find posted notification id=" + id); 2000 } 2001 } 2002 testInboxStyle()2003 public void testInboxStyle() { 2004 final int id = 100; 2005 2006 final Notification notification = 2007 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 2008 .setSmallIcon(R.drawable.black) 2009 .setContentTitle("notify#" + id) 2010 .setContentText("This is #" + id + "notification ") 2011 .addAction(new Notification.Action.Builder( 2012 Icon.createWithResource(getContext(), R.drawable.icon_black), 2013 "a1", getPendingIntent()).build()) 2014 .addAction(new Notification.Action.Builder( 2015 Icon.createWithResource(getContext(), R.drawable.icon_blue), 2016 "a2", getPendingIntent()).build()) 2017 .setStyle(new Notification.InboxStyle().addLine("line") 2018 .setSummaryText("summary")) 2019 .build(); 2020 mNotificationManager.notify(id, notification); 2021 2022 if (!checkNotificationExistence(id, /*shouldExist=*/ true)) { 2023 fail("couldn't find posted notification id=" + id); 2024 } 2025 } 2026 testBigTextStyle()2027 public void testBigTextStyle() { 2028 final int id = 101; 2029 2030 final Notification notification = 2031 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 2032 .setSmallIcon(R.drawable.black) 2033 .setContentTitle("notify#" + id) 2034 .setContentText("This is #" + id + "notification ") 2035 .addAction(new Notification.Action.Builder( 2036 Icon.createWithResource(getContext(), R.drawable.icon_black), 2037 "a1", getPendingIntent()).build()) 2038 .addAction(new Notification.Action.Builder( 2039 Icon.createWithResource(getContext(), R.drawable.icon_blue), 2040 "a2", getPendingIntent()).build()) 2041 .setStyle(new Notification.BigTextStyle() 2042 .setBigContentTitle("big title") 2043 .bigText("big text") 2044 .setSummaryText("summary")) 2045 .build(); 2046 mNotificationManager.notify(id, notification); 2047 2048 if (!checkNotificationExistence(id, /*shouldExist=*/ true)) { 2049 fail("couldn't find posted notification id=" + id); 2050 } 2051 } 2052 testBigPictureStyle()2053 public void testBigPictureStyle() { 2054 final int id = 102; 2055 2056 final Notification notification = 2057 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 2058 .setSmallIcon(R.drawable.black) 2059 .setContentTitle("notify#" + id) 2060 .setContentText("This is #" + id + "notification ") 2061 .addAction(new Notification.Action.Builder( 2062 Icon.createWithResource(getContext(), R.drawable.icon_black), 2063 "a1", getPendingIntent()).build()) 2064 .addAction(new Notification.Action.Builder( 2065 Icon.createWithResource(getContext(), R.drawable.icon_blue), 2066 "a2", getPendingIntent()).build()) 2067 .setStyle(new Notification.BigPictureStyle() 2068 .setBigContentTitle("title") 2069 .bigPicture(Bitmap.createBitmap(100, 100, Bitmap.Config.RGB_565)) 2070 .bigLargeIcon( 2071 Icon.createWithResource(getContext(), R.drawable.icon_blue)) 2072 .setSummaryText("summary") 2073 .setContentDescription("content description")) 2074 .build(); 2075 mNotificationManager.notify(id, notification); 2076 2077 if (!checkNotificationExistence(id, /*shouldExist=*/ true)) { 2078 fail("couldn't find posted notification id=" + id); 2079 } 2080 } 2081 testAutogrouping()2082 public void testAutogrouping() throws Exception { 2083 sendNotification(801, R.drawable.black); 2084 sendNotification(802, R.drawable.blue); 2085 sendNotification(803, R.drawable.yellow); 2086 sendNotification(804, R.drawable.yellow); 2087 2088 assertNotificationCount(5); 2089 assertAllPostedNotificationsAutogrouped(); 2090 } 2091 testAutogrouping_autogroupStaysUntilAllNotificationsCanceled()2092 public void testAutogrouping_autogroupStaysUntilAllNotificationsCanceled() throws Exception { 2093 sendNotification(701, R.drawable.black); 2094 sendNotification(702, R.drawable.blue); 2095 sendNotification(703, R.drawable.yellow); 2096 sendNotification(704, R.drawable.yellow); 2097 2098 assertNotificationCount(5); 2099 assertAllPostedNotificationsAutogrouped(); 2100 2101 // Assert all notis stay in the same autogroup until all children are canceled 2102 for (int i = 704; i > 701; i--) { 2103 cancelAndPoll(i); 2104 assertNotificationCount(i - 700); 2105 assertAllPostedNotificationsAutogrouped(); 2106 } 2107 cancelAndPoll(701); 2108 assertNotificationCount(0); 2109 } 2110 testAutogrouping_autogroupStaysUntilAllNotificationsAddedToGroup()2111 public void testAutogrouping_autogroupStaysUntilAllNotificationsAddedToGroup() 2112 throws Exception { 2113 String newGroup = "new!"; 2114 sendNotification(901, R.drawable.black); 2115 sendNotification(902, R.drawable.blue); 2116 sendNotification(903, R.drawable.yellow); 2117 sendNotification(904, R.drawable.yellow); 2118 2119 List<Integer> postedIds = new ArrayList<>(); 2120 postedIds.add(901); 2121 postedIds.add(902); 2122 postedIds.add(903); 2123 postedIds.add(904); 2124 2125 assertNotificationCount(5); 2126 assertAllPostedNotificationsAutogrouped(); 2127 2128 // Assert all notis stay in the same autogroup until all children are canceled 2129 for (int i = 904; i > 901; i--) { 2130 sendNotification(i, newGroup, R.drawable.blue); 2131 postedIds.remove(postedIds.size() - 1); 2132 assertNotificationCount(5); 2133 assertOnlySomeNotificationsAutogrouped(postedIds); 2134 } 2135 sendNotification(901, newGroup, R.drawable.blue); 2136 assertNotificationCount(4); // no more autogroup summary 2137 postedIds.remove(0); 2138 assertOnlySomeNotificationsAutogrouped(postedIds); 2139 } 2140 testNewNotificationsAddedToAutogroup_ifOriginalNotificationsCanceled()2141 public void testNewNotificationsAddedToAutogroup_ifOriginalNotificationsCanceled() 2142 throws Exception { 2143 String newGroup = "new!"; 2144 sendNotification(910, R.drawable.black); 2145 sendNotification(920, R.drawable.blue); 2146 sendNotification(930, R.drawable.yellow); 2147 sendNotification(940, R.drawable.yellow); 2148 2149 List<Integer> postedIds = new ArrayList<>(); 2150 postedIds.add(910); 2151 postedIds.add(920); 2152 postedIds.add(930); 2153 postedIds.add(940); 2154 2155 assertNotificationCount(5); 2156 assertAllPostedNotificationsAutogrouped(); 2157 2158 // regroup all but one of the children 2159 for (int i = postedIds.size() - 1; i > 0; i--) { 2160 try { 2161 Thread.sleep(200); 2162 } catch (InterruptedException ex) { 2163 // pass 2164 } 2165 int id = postedIds.remove(i); 2166 sendNotification(id, newGroup, R.drawable.blue); 2167 assertNotificationCount(5); 2168 assertOnlySomeNotificationsAutogrouped(postedIds); 2169 } 2170 2171 // send a new non-grouped notification. since the autogroup summary still exists, 2172 // the notification should be added to it 2173 sendNotification(950, R.drawable.blue); 2174 postedIds.add(950); 2175 try { 2176 Thread.sleep(200); 2177 } catch (InterruptedException ex) { 2178 // pass 2179 } 2180 assertOnlySomeNotificationsAutogrouped(postedIds); 2181 } 2182 testTotalSilenceOnlyMuteStreams()2183 public void testTotalSilenceOnlyMuteStreams() throws Exception { 2184 final int originalFilter = mNotificationManager.getCurrentInterruptionFilter(); 2185 Policy origPolicy = mNotificationManager.getNotificationPolicy(); 2186 try { 2187 toggleNotificationPolicyAccess(mContext.getPackageName(), 2188 InstrumentationRegistry.getInstrumentation(), true); 2189 2190 // ensure volume is not muted/0 to start test 2191 mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 1, 0); 2192 // exception for presidential alert 2193 //mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, 1, 0); 2194 mAudioManager.setStreamVolume(AudioManager.STREAM_SYSTEM, 1, 0); 2195 mAudioManager.setStreamVolume(AudioManager.STREAM_RING, 1, 0); 2196 2197 mNotificationManager.setNotificationPolicy(new NotificationManager.Policy( 2198 PRIORITY_CATEGORY_ALARMS | PRIORITY_CATEGORY_MEDIA, 0, 0)); 2199 AutomaticZenRule rule = createRule("test_total_silence", INTERRUPTION_FILTER_NONE); 2200 String id = mNotificationManager.addAutomaticZenRule(rule); 2201 mRuleIds.add(id); 2202 Condition condition = 2203 new Condition(rule.getConditionId(), "summary", Condition.STATE_TRUE); 2204 mNotificationManager.setAutomaticZenRuleState(id, condition); 2205 mNotificationManager.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY); 2206 2207 // delay for streams to get into correct mute states 2208 Thread.sleep(1000); 2209 assertTrue("Music (media) stream should be muted", 2210 mAudioManager.isStreamMute(AudioManager.STREAM_MUSIC)); 2211 assertTrue("System stream should be muted", 2212 mAudioManager.isStreamMute(AudioManager.STREAM_SYSTEM)); 2213 // exception for presidential alert 2214 //assertTrue("Alarm stream should be muted", 2215 // mAudioManager.isStreamMute(AudioManager.STREAM_ALARM)); 2216 2217 // Test requires that the phone's default state has no channels that can bypass dnd 2218 // which we can't currently guarantee (b/169267379) 2219 // assertTrue("Ringer stream should be muted", 2220 // mAudioManager.isStreamMute(AudioManager.STREAM_RING)); 2221 } finally { 2222 mNotificationManager.setInterruptionFilter(originalFilter); 2223 mNotificationManager.setNotificationPolicy(origPolicy); 2224 } 2225 } 2226 testAlarmsOnlyMuteStreams()2227 public void testAlarmsOnlyMuteStreams() throws Exception { 2228 final int originalFilter = mNotificationManager.getCurrentInterruptionFilter(); 2229 Policy origPolicy = mNotificationManager.getNotificationPolicy(); 2230 try { 2231 toggleNotificationPolicyAccess(mContext.getPackageName(), 2232 InstrumentationRegistry.getInstrumentation(), true); 2233 2234 // ensure volume is not muted/0 to start test 2235 mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 1, 0); 2236 mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, 1, 0); 2237 mAudioManager.setStreamVolume(AudioManager.STREAM_SYSTEM, 1, 0); 2238 mAudioManager.setStreamVolume(AudioManager.STREAM_RING, 1, 0); 2239 2240 mNotificationManager.setNotificationPolicy(new NotificationManager.Policy( 2241 PRIORITY_CATEGORY_ALARMS | PRIORITY_CATEGORY_MEDIA, 0, 0)); 2242 AutomaticZenRule rule = createRule("test_alarms", INTERRUPTION_FILTER_ALARMS); 2243 String id = mNotificationManager.addAutomaticZenRule(rule); 2244 mRuleIds.add(id); 2245 Condition condition = 2246 new Condition(rule.getConditionId(), "summary", Condition.STATE_TRUE); 2247 mNotificationManager.setAutomaticZenRuleState(id, condition); 2248 mNotificationManager.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY); 2249 2250 // delay for streams to get into correct mute states 2251 Thread.sleep(1000); 2252 assertFalse("Music (media) stream should not be muted", 2253 mAudioManager.isStreamMute(AudioManager.STREAM_MUSIC)); 2254 assertTrue("System stream should be muted", 2255 mAudioManager.isStreamMute(AudioManager.STREAM_SYSTEM)); 2256 assertFalse("Alarm stream should not be muted", 2257 mAudioManager.isStreamMute(AudioManager.STREAM_ALARM)); 2258 2259 // Test requires that the phone's default state has no channels that can bypass dnd 2260 // which we can't currently guarantee (b/169267379) 2261 // assertTrue("Ringer stream should be muted", 2262 // mAudioManager.isStreamMute(AudioManager.STREAM_RING)); 2263 } finally { 2264 mNotificationManager.setInterruptionFilter(originalFilter); 2265 mNotificationManager.setNotificationPolicy(origPolicy); 2266 } 2267 } 2268 testAddAutomaticZenRule_configActivity()2269 public void testAddAutomaticZenRule_configActivity() throws Exception { 2270 toggleNotificationPolicyAccess(mContext.getPackageName(), 2271 InstrumentationRegistry.getInstrumentation(), true); 2272 2273 AutomaticZenRule ruleToCreate = createRule("Rule"); 2274 String id = mNotificationManager.addAutomaticZenRule(ruleToCreate); 2275 2276 assertNotNull(id); 2277 mRuleIds.add(id); 2278 assertTrue(areRulesSame(ruleToCreate, mNotificationManager.getAutomaticZenRule(id))); 2279 } 2280 testUpdateAutomaticZenRule_configActivity()2281 public void testUpdateAutomaticZenRule_configActivity() throws Exception { 2282 toggleNotificationPolicyAccess(mContext.getPackageName(), 2283 InstrumentationRegistry.getInstrumentation(), true); 2284 2285 AutomaticZenRule ruleToCreate = createRule("Rule"); 2286 String id = mNotificationManager.addAutomaticZenRule(ruleToCreate); 2287 ruleToCreate.setEnabled(false); 2288 mNotificationManager.updateAutomaticZenRule(id, ruleToCreate); 2289 2290 assertNotNull(id); 2291 mRuleIds.add(id); 2292 assertTrue(areRulesSame(ruleToCreate, mNotificationManager.getAutomaticZenRule(id))); 2293 } 2294 testRemoveAutomaticZenRule_configActivity()2295 public void testRemoveAutomaticZenRule_configActivity() throws Exception { 2296 toggleNotificationPolicyAccess(mContext.getPackageName(), 2297 InstrumentationRegistry.getInstrumentation(), true); 2298 2299 AutomaticZenRule ruleToCreate = createRule("Rule"); 2300 String id = mNotificationManager.addAutomaticZenRule(ruleToCreate); 2301 2302 assertNotNull(id); 2303 mRuleIds.add(id); 2304 mNotificationManager.removeAutomaticZenRule(id); 2305 2306 assertNull(mNotificationManager.getAutomaticZenRule(id)); 2307 assertEquals(0, mNotificationManager.getAutomaticZenRules().size()); 2308 } 2309 testSetAutomaticZenRuleState()2310 public void testSetAutomaticZenRuleState() throws Exception { 2311 toggleNotificationPolicyAccess(mContext.getPackageName(), 2312 InstrumentationRegistry.getInstrumentation(), true); 2313 2314 AutomaticZenRule ruleToCreate = createRule("Rule"); 2315 String id = mNotificationManager.addAutomaticZenRule(ruleToCreate); 2316 mRuleIds.add(id); 2317 2318 // make sure DND is off 2319 assertExpectedDndState(INTERRUPTION_FILTER_ALL); 2320 2321 // enable DND 2322 Condition condition = 2323 new Condition(ruleToCreate.getConditionId(), "summary", Condition.STATE_TRUE); 2324 mNotificationManager.setAutomaticZenRuleState(id, condition); 2325 2326 assertExpectedDndState(ruleToCreate.getInterruptionFilter()); 2327 } 2328 testSetAutomaticZenRuleState_turnOff()2329 public void testSetAutomaticZenRuleState_turnOff() throws Exception { 2330 toggleNotificationPolicyAccess(mContext.getPackageName(), 2331 InstrumentationRegistry.getInstrumentation(), true); 2332 2333 AutomaticZenRule ruleToCreate = createRule("Rule"); 2334 String id = mNotificationManager.addAutomaticZenRule(ruleToCreate); 2335 mRuleIds.add(id); 2336 2337 // make sure DND is off 2338 // make sure DND is off 2339 assertExpectedDndState(INTERRUPTION_FILTER_ALL); 2340 2341 // enable DND 2342 Condition condition = 2343 new Condition(ruleToCreate.getConditionId(), "on", Condition.STATE_TRUE); 2344 mNotificationManager.setAutomaticZenRuleState(id, condition); 2345 2346 assertExpectedDndState(ruleToCreate.getInterruptionFilter()); 2347 2348 // disable DND 2349 condition = new Condition(ruleToCreate.getConditionId(), "off", Condition.STATE_FALSE); 2350 2351 mNotificationManager.setAutomaticZenRuleState(id, condition); 2352 2353 // make sure DND is off 2354 assertExpectedDndState(INTERRUPTION_FILTER_ALL); 2355 } 2356 testSetAutomaticZenRuleState_deletedRule()2357 public void testSetAutomaticZenRuleState_deletedRule() throws Exception { 2358 toggleNotificationPolicyAccess(mContext.getPackageName(), 2359 InstrumentationRegistry.getInstrumentation(), true); 2360 2361 AutomaticZenRule ruleToCreate = createRule("Rule"); 2362 String id = mNotificationManager.addAutomaticZenRule(ruleToCreate); 2363 mRuleIds.add(id); 2364 2365 // make sure DND is off 2366 assertExpectedDndState(INTERRUPTION_FILTER_ALL); 2367 2368 // enable DND 2369 Condition condition = 2370 new Condition(ruleToCreate.getConditionId(), "summary", Condition.STATE_TRUE); 2371 mNotificationManager.setAutomaticZenRuleState(id, condition); 2372 2373 assertExpectedDndState(ruleToCreate.getInterruptionFilter()); 2374 2375 mNotificationManager.removeAutomaticZenRule(id); 2376 2377 // make sure DND is off 2378 assertExpectedDndState(INTERRUPTION_FILTER_ALL); 2379 } 2380 testSetAutomaticZenRuleState_multipleRules()2381 public void testSetAutomaticZenRuleState_multipleRules() throws Exception { 2382 toggleNotificationPolicyAccess(mContext.getPackageName(), 2383 InstrumentationRegistry.getInstrumentation(), true); 2384 2385 AutomaticZenRule ruleToCreate = createRule("Rule"); 2386 String id = mNotificationManager.addAutomaticZenRule(ruleToCreate); 2387 mRuleIds.add(id); 2388 2389 AutomaticZenRule secondRuleToCreate = createRule("Rule 2"); 2390 secondRuleToCreate.setInterruptionFilter(INTERRUPTION_FILTER_NONE); 2391 String secondId = mNotificationManager.addAutomaticZenRule(secondRuleToCreate); 2392 mRuleIds.add(secondId); 2393 2394 // make sure DND is off 2395 assertExpectedDndState(INTERRUPTION_FILTER_ALL); 2396 2397 // enable DND 2398 Condition condition = 2399 new Condition(ruleToCreate.getConditionId(), "summary", Condition.STATE_TRUE); 2400 mNotificationManager.setAutomaticZenRuleState(id, condition); 2401 Condition secondCondition = 2402 new Condition(secondRuleToCreate.getConditionId(), "summary", Condition.STATE_TRUE); 2403 mNotificationManager.setAutomaticZenRuleState(secondId, secondCondition); 2404 2405 // the second rule has a 'more silent' DND filter, so the system wide DND should be 2406 // using its filter 2407 assertExpectedDndState(secondRuleToCreate.getInterruptionFilter()); 2408 2409 // remove intense rule, system should fallback to other rule 2410 mNotificationManager.removeAutomaticZenRule(secondId); 2411 assertExpectedDndState(ruleToCreate.getInterruptionFilter()); 2412 } 2413 testSetNotificationPolicy_P_setOldFields()2414 public void testSetNotificationPolicy_P_setOldFields() throws Exception { 2415 toggleNotificationPolicyAccess(mContext.getPackageName(), 2416 InstrumentationRegistry.getInstrumentation(), true); 2417 Policy origPolicy = mNotificationManager.getNotificationPolicy(); 2418 try { 2419 if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.P) { 2420 NotificationManager.Policy appPolicy = new NotificationManager.Policy(0, 0, 0, 2421 SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_SCREEN_OFF); 2422 mNotificationManager.setNotificationPolicy(appPolicy); 2423 2424 int expected = SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_SCREEN_OFF 2425 | SUPPRESSED_EFFECT_PEEK | SUPPRESSED_EFFECT_AMBIENT 2426 | SUPPRESSED_EFFECT_LIGHTS | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT; 2427 2428 assertEquals(expected, 2429 mNotificationManager.getNotificationPolicy().suppressedVisualEffects); 2430 } 2431 } finally { 2432 mNotificationManager.setNotificationPolicy(origPolicy); 2433 } 2434 } 2435 testSetNotificationPolicy_P_setNewFields()2436 public void testSetNotificationPolicy_P_setNewFields() throws Exception { 2437 toggleNotificationPolicyAccess(mContext.getPackageName(), 2438 InstrumentationRegistry.getInstrumentation(), true); 2439 Policy origPolicy = mNotificationManager.getNotificationPolicy(); 2440 try { 2441 if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.P) { 2442 NotificationManager.Policy appPolicy = new NotificationManager.Policy(0, 0, 0, 2443 SUPPRESSED_EFFECT_NOTIFICATION_LIST | SUPPRESSED_EFFECT_AMBIENT 2444 | SUPPRESSED_EFFECT_LIGHTS | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT); 2445 mNotificationManager.setNotificationPolicy(appPolicy); 2446 2447 int expected = SUPPRESSED_EFFECT_NOTIFICATION_LIST | SUPPRESSED_EFFECT_SCREEN_OFF 2448 | SUPPRESSED_EFFECT_AMBIENT | SUPPRESSED_EFFECT_LIGHTS 2449 | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT; 2450 assertEquals(expected, 2451 mNotificationManager.getNotificationPolicy().suppressedVisualEffects); 2452 } 2453 } finally { 2454 mNotificationManager.setNotificationPolicy(origPolicy); 2455 } 2456 } 2457 testSetNotificationPolicy_P_setOldNewFields()2458 public void testSetNotificationPolicy_P_setOldNewFields() throws Exception { 2459 toggleNotificationPolicyAccess(mContext.getPackageName(), 2460 InstrumentationRegistry.getInstrumentation(), true); 2461 Policy origPolicy = mNotificationManager.getNotificationPolicy(); 2462 try { 2463 if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.P) { 2464 2465 NotificationManager.Policy appPolicy = new NotificationManager.Policy(0, 0, 0, 2466 SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_STATUS_BAR); 2467 mNotificationManager.setNotificationPolicy(appPolicy); 2468 2469 int expected = SUPPRESSED_EFFECT_STATUS_BAR; 2470 assertEquals(expected, 2471 mNotificationManager.getNotificationPolicy().suppressedVisualEffects); 2472 2473 appPolicy = new NotificationManager.Policy(0, 0, 0, 2474 SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_AMBIENT 2475 | SUPPRESSED_EFFECT_LIGHTS | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT); 2476 mNotificationManager.setNotificationPolicy(appPolicy); 2477 2478 expected = SUPPRESSED_EFFECT_SCREEN_OFF | SUPPRESSED_EFFECT_AMBIENT 2479 | SUPPRESSED_EFFECT_LIGHTS | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT; 2480 assertEquals(expected, 2481 mNotificationManager.getNotificationPolicy().suppressedVisualEffects); 2482 } 2483 } finally { 2484 mNotificationManager.setNotificationPolicy(origPolicy); 2485 } 2486 } 2487 testPostFullScreenIntent_permission()2488 public void testPostFullScreenIntent_permission() { 2489 int id = 6000; 2490 2491 final Notification notification = 2492 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 2493 .setSmallIcon(R.drawable.black) 2494 .setWhen(System.currentTimeMillis()) 2495 .setFullScreenIntent(getPendingIntent(), true) 2496 .setContentText("This is #FSI notification") 2497 .setContentIntent(getPendingIntent()) 2498 .build(); 2499 mNotificationManager.notify(id, notification); 2500 2501 StatusBarNotification n = findPostedNotification(id, false); 2502 assertNotNull(n); 2503 assertEquals(notification.fullScreenIntent, n.getNotification().fullScreenIntent); 2504 } 2505 testNotificationPolicyVisualEffectsEqual()2506 public void testNotificationPolicyVisualEffectsEqual() { 2507 NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0, 2508 SUPPRESSED_EFFECT_SCREEN_ON); 2509 NotificationManager.Policy policy2 = new NotificationManager.Policy(0, 0, 0, 2510 SUPPRESSED_EFFECT_PEEK); 2511 assertTrue(policy.equals(policy2)); 2512 assertTrue(policy2.equals(policy)); 2513 2514 policy = new NotificationManager.Policy(0, 0, 0, 2515 SUPPRESSED_EFFECT_SCREEN_ON); 2516 policy2 = new NotificationManager.Policy(0, 0, 0, 2517 0); 2518 assertFalse(policy.equals(policy2)); 2519 assertFalse(policy2.equals(policy)); 2520 2521 policy = new NotificationManager.Policy(0, 0, 0, 2522 SUPPRESSED_EFFECT_SCREEN_OFF); 2523 policy2 = new NotificationManager.Policy(0, 0, 0, 2524 SUPPRESSED_EFFECT_FULL_SCREEN_INTENT | SUPPRESSED_EFFECT_AMBIENT 2525 | SUPPRESSED_EFFECT_LIGHTS); 2526 assertTrue(policy.equals(policy2)); 2527 assertTrue(policy2.equals(policy)); 2528 2529 policy = new NotificationManager.Policy(0, 0, 0, 2530 SUPPRESSED_EFFECT_SCREEN_OFF); 2531 policy2 = new NotificationManager.Policy(0, 0, 0, 2532 SUPPRESSED_EFFECT_LIGHTS); 2533 assertFalse(policy.equals(policy2)); 2534 assertFalse(policy2.equals(policy)); 2535 } 2536 testNotificationDelegate_grantAndPost()2537 public void testNotificationDelegate_grantAndPost() throws Exception { 2538 // grant this test permission to post 2539 final Intent activityIntent = new Intent(); 2540 activityIntent.setPackage(DELEGATOR); 2541 activityIntent.setAction(Intent.ACTION_MAIN); 2542 activityIntent.addCategory(Intent.CATEGORY_LAUNCHER); 2543 activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 2544 2545 // wait for the activity to launch and finish 2546 mContext.startActivity(activityIntent); 2547 Thread.sleep(mActivityManager.isLowRamDevice() ? 1500 : 1000); 2548 2549 // send notification 2550 Notification n = new Notification.Builder(mContext, "channel") 2551 .setSmallIcon(android.R.id.icon) 2552 .build(); 2553 mNotificationManager.notifyAsPackage(DELEGATOR, "tag", 0, n); 2554 2555 assertNotNull(findPostedNotification(0, false)); 2556 final Intent revokeIntent = new Intent(); 2557 revokeIntent.setClassName(DELEGATOR, REVOKE_CLASS); 2558 revokeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 2559 mContext.startActivity(revokeIntent); 2560 Thread.sleep(1000); 2561 } 2562 testNotificationDelegate_grantAndPostAndCancel()2563 public void testNotificationDelegate_grantAndPostAndCancel() throws Exception { 2564 // grant this test permission to post 2565 final Intent activityIntent = new Intent(); 2566 activityIntent.setPackage(DELEGATOR); 2567 activityIntent.setAction(Intent.ACTION_MAIN); 2568 activityIntent.addCategory(Intent.CATEGORY_LAUNCHER); 2569 activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 2570 2571 // wait for the activity to launch and finish 2572 mContext.startActivity(activityIntent); 2573 Thread.sleep(1000); 2574 2575 // send notification 2576 Notification n = new Notification.Builder(mContext, "channel") 2577 .setSmallIcon(android.R.id.icon) 2578 .build(); 2579 mNotificationManager.notifyAsPackage(DELEGATOR, "toBeCanceled", 10000, n); 2580 assertNotNull(findPostedNotification(10000, false)); 2581 mNotificationManager.cancelAsPackage(DELEGATOR, "toBeCanceled", 10000); 2582 assertNotificationCancelled(10000, false); 2583 final Intent revokeIntent = new Intent(); 2584 revokeIntent.setClassName(DELEGATOR, REVOKE_CLASS); 2585 revokeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 2586 mContext.startActivity(revokeIntent); 2587 Thread.sleep(1000); 2588 } 2589 testNotificationDelegate_cannotCancelNotificationsPostedByDelegator()2590 public void testNotificationDelegate_cannotCancelNotificationsPostedByDelegator() 2591 throws Exception { 2592 toggleListenerAccess(true); 2593 Thread.sleep(500); // wait for listener to be allowed 2594 2595 mListener = TestNotificationListener.getInstance(); 2596 assertNotNull(mListener); 2597 2598 // grant this test permission to post 2599 final Intent activityIntent = new Intent(); 2600 activityIntent.setClassName(DELEGATOR, DELEGATE_POST_CLASS); 2601 activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 2602 2603 mContext.startActivity(activityIntent); 2604 2605 Thread.sleep(1000); 2606 2607 assertNotNull(findPostedNotification(9, true)); 2608 2609 try { 2610 mNotificationManager.cancelAsPackage(DELEGATOR, null, 9); 2611 fail("Delegate should not be able to cancel notification they did not post"); 2612 } catch (SecurityException e) { 2613 // yay 2614 } 2615 2616 // double check that the notification does still exist 2617 assertNotNull(findPostedNotification(9, true)); 2618 2619 final Intent revokeIntent = new Intent(); 2620 revokeIntent.setClassName(DELEGATOR, REVOKE_CLASS); 2621 revokeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 2622 mContext.startActivity(revokeIntent); 2623 Thread.sleep(1000); 2624 } 2625 testNotificationDelegate_grantAndReadChannels()2626 public void testNotificationDelegate_grantAndReadChannels() throws Exception { 2627 // grant this test permission to post 2628 final Intent activityIntent = new Intent(); 2629 activityIntent.setPackage(DELEGATOR); 2630 activityIntent.setAction(Intent.ACTION_MAIN); 2631 activityIntent.addCategory(Intent.CATEGORY_LAUNCHER); 2632 activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 2633 2634 // wait for the activity to launch and finish 2635 mContext.startActivity(activityIntent); 2636 Thread.sleep(500); 2637 2638 List<NotificationChannel> channels = 2639 mContext.createPackageContextAsUser(DELEGATOR, /* flags= */ 0, mContext.getUser()) 2640 .getSystemService(NotificationManager.class) 2641 .getNotificationChannels(); 2642 2643 assertNotNull(channels); 2644 2645 final Intent revokeIntent = new Intent(); 2646 revokeIntent.setClassName(DELEGATOR, REVOKE_CLASS); 2647 revokeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 2648 mContext.startActivity(revokeIntent); 2649 Thread.sleep(500); 2650 } 2651 testNotificationDelegate_grantAndReadChannel()2652 public void testNotificationDelegate_grantAndReadChannel() throws Exception { 2653 // grant this test permission to post 2654 final Intent activityIntent = new Intent(); 2655 activityIntent.setPackage(DELEGATOR); 2656 activityIntent.setAction(Intent.ACTION_MAIN); 2657 activityIntent.addCategory(Intent.CATEGORY_LAUNCHER); 2658 activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 2659 2660 // wait for the activity to launch and finish 2661 mContext.startActivity(activityIntent); 2662 Thread.sleep(2000); 2663 2664 NotificationChannel channel = 2665 mContext.createPackageContextAsUser(DELEGATOR, /* flags= */ 0, mContext.getUser()) 2666 .getSystemService(NotificationManager.class) 2667 .getNotificationChannel("channel"); 2668 2669 assertNotNull(channel); 2670 2671 final Intent revokeIntent = new Intent(); 2672 revokeIntent.setClassName(DELEGATOR, REVOKE_CLASS); 2673 revokeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 2674 mContext.startActivity(revokeIntent); 2675 Thread.sleep(500); 2676 } 2677 testNotificationDelegate_grantAndRevoke()2678 public void testNotificationDelegate_grantAndRevoke() throws Exception { 2679 // grant this test permission to post 2680 final Intent activityIntent = new Intent(); 2681 activityIntent.setPackage(DELEGATOR); 2682 activityIntent.setAction(Intent.ACTION_MAIN); 2683 activityIntent.addCategory(Intent.CATEGORY_LAUNCHER); 2684 activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 2685 2686 mContext.startActivity(activityIntent); 2687 Thread.sleep(500); 2688 2689 assertTrue(mNotificationManager.canNotifyAsPackage(DELEGATOR)); 2690 2691 final Intent revokeIntent = new Intent(); 2692 revokeIntent.setClassName(DELEGATOR, REVOKE_CLASS); 2693 revokeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 2694 mContext.startActivity(revokeIntent); 2695 Thread.sleep(500); 2696 2697 try { 2698 // send notification 2699 Notification n = new Notification.Builder(mContext, "channel") 2700 .setSmallIcon(android.R.id.icon) 2701 .build(); 2702 mNotificationManager.notifyAsPackage(DELEGATOR, "tag", 0, n); 2703 fail("Should not be able to post as a delegate when permission revoked"); 2704 } catch (SecurityException e) { 2705 // yay 2706 } 2707 } 2708 testAreBubblesAllowed_appNone()2709 public void testAreBubblesAllowed_appNone() throws Exception { 2710 setBubblesAppPref(BUBBLE_PREFERENCE_NONE); 2711 assertFalse(mNotificationManager.areBubblesAllowed()); 2712 } 2713 testAreBubblesAllowed_appSelected()2714 public void testAreBubblesAllowed_appSelected() throws Exception { 2715 setBubblesAppPref(BUBBLE_PREFERENCE_SELECTED); 2716 assertFalse(mNotificationManager.areBubblesAllowed()); 2717 } 2718 testAreBubblesAllowed_appAll()2719 public void testAreBubblesAllowed_appAll() throws Exception { 2720 setBubblesAppPref(BUBBLE_PREFERENCE_ALL); 2721 assertTrue(mNotificationManager.areBubblesAllowed()); 2722 } 2723 testGetBubblePreference_appNone()2724 public void testGetBubblePreference_appNone() throws Exception { 2725 setBubblesAppPref(BUBBLE_PREFERENCE_NONE); 2726 assertEquals(BUBBLE_PREFERENCE_NONE, mNotificationManager.getBubblePreference()); 2727 } 2728 testGetBubblePreference_appSelected()2729 public void testGetBubblePreference_appSelected() throws Exception { 2730 setBubblesAppPref(BUBBLE_PREFERENCE_SELECTED); 2731 assertEquals(BUBBLE_PREFERENCE_SELECTED, mNotificationManager.getBubblePreference()); 2732 } 2733 testGetBubblePreference_appAll()2734 public void testGetBubblePreference_appAll() throws Exception { 2735 setBubblesAppPref(BUBBLE_PREFERENCE_ALL); 2736 assertEquals(BUBBLE_PREFERENCE_ALL, mNotificationManager.getBubblePreference()); 2737 } 2738 testAreBubblesEnabled()2739 public void testAreBubblesEnabled() throws Exception { 2740 setBubblesGlobal(true); 2741 assertTrue(mNotificationManager.areBubblesEnabled()); 2742 } 2743 testAreBubblesEnabled_false()2744 public void testAreBubblesEnabled_false() throws Exception { 2745 setBubblesGlobal(false); 2746 assertFalse(mNotificationManager.areBubblesEnabled()); 2747 } 2748 testNotificationIcon()2749 public void testNotificationIcon() { 2750 int id = 6000; 2751 2752 Notification notification = 2753 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 2754 .setSmallIcon(android.R.id.icon) 2755 .setWhen(System.currentTimeMillis()) 2756 .setFullScreenIntent(getPendingIntent(), true) 2757 .setContentText("This notification has a resource icon") 2758 .setContentIntent(getPendingIntent()) 2759 .build(); 2760 mNotificationManager.notify(id, notification); 2761 2762 notification = 2763 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 2764 .setSmallIcon(Icon.createWithResource(mContext, android.R.id.icon)) 2765 .setWhen(System.currentTimeMillis()) 2766 .setFullScreenIntent(getPendingIntent(), true) 2767 .setContentText("This notification has an Icon icon") 2768 .setContentIntent(getPendingIntent()) 2769 .build(); 2770 mNotificationManager.notify(id, notification); 2771 2772 StatusBarNotification n = findPostedNotification(id, false); 2773 assertNotNull(n); 2774 } 2775 testShouldHideSilentStatusIcons()2776 public void testShouldHideSilentStatusIcons() throws Exception { 2777 try { 2778 mNotificationManager.shouldHideSilentStatusBarIcons(); 2779 fail("Non-privileged apps should not get this information"); 2780 } catch (SecurityException e) { 2781 // pass 2782 } 2783 2784 toggleListenerAccess(true); 2785 // no exception this time 2786 mNotificationManager.shouldHideSilentStatusBarIcons(); 2787 } 2788 testMatchesCallFilter()2789 public void testMatchesCallFilter() throws Exception { 2790 // allow all callers 2791 toggleNotificationPolicyAccess(mContext.getPackageName(), 2792 InstrumentationRegistry.getInstrumentation(), true); 2793 Policy origPolicy = mNotificationManager.getNotificationPolicy(); 2794 Uri aliceUri = null; 2795 try { 2796 NotificationManager.Policy currPolicy = mNotificationManager.getNotificationPolicy(); 2797 NotificationManager.Policy newPolicy = new NotificationManager.Policy( 2798 NotificationManager.Policy.PRIORITY_CATEGORY_CALLS 2799 | NotificationManager.Policy.PRIORITY_CATEGORY_REPEAT_CALLERS, 2800 NotificationManager.Policy.PRIORITY_SENDERS_ANY, 2801 currPolicy.priorityMessageSenders, 2802 currPolicy.suppressedVisualEffects); 2803 mNotificationManager.setNotificationPolicy(newPolicy); 2804 2805 // add a contact 2806 String ALICE = "Alice"; 2807 String ALICE_PHONE = "+16175551212"; 2808 String ALICE_EMAIL = "alice@_foo._bar"; 2809 2810 insertSingleContact(ALICE, ALICE_PHONE, ALICE_EMAIL, false); 2811 2812 final Bundle peopleExtras = new Bundle(); 2813 ArrayList<Person> personList = new ArrayList<>(); 2814 aliceUri = lookupContact(ALICE_PHONE); 2815 personList.add(new Person.Builder().setUri(aliceUri.toString()).build()); 2816 peopleExtras.putParcelableArrayList(Notification.EXTRA_PEOPLE_LIST, personList); 2817 SystemUtil.runWithShellPermissionIdentity(() -> 2818 assertTrue(mNotificationManager.matchesCallFilter(peopleExtras))); 2819 } finally { 2820 mNotificationManager.setNotificationPolicy(origPolicy); 2821 if (aliceUri != null) { 2822 // delete the contact 2823 deleteSingleContact(aliceUri); 2824 } 2825 } 2826 2827 } 2828 2829 /* Confirm that the optional methods of TestNotificationListener still exist and 2830 * don't fail. */ testNotificationListenerMethods()2831 public void testNotificationListenerMethods() { 2832 NotificationListenerService listener = new TestNotificationListener(); 2833 listener.onListenerConnected(); 2834 2835 listener.onSilentStatusBarIconsVisibilityChanged(false); 2836 2837 listener.onNotificationPosted(null); 2838 listener.onNotificationPosted(null, null); 2839 2840 listener.onNotificationRemoved(null); 2841 listener.onNotificationRemoved(null, null); 2842 2843 listener.onNotificationChannelGroupModified("", UserHandle.CURRENT, null, 2844 NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_ADDED); 2845 listener.onNotificationChannelModified("", UserHandle.CURRENT, null, 2846 NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_ADDED); 2847 2848 listener.onListenerDisconnected(); 2849 } 2850 performNotificationProviderAction(@onNull String action)2851 private void performNotificationProviderAction(@NonNull String action) { 2852 // Create an intent to launch an activity which just posts or cancels notifications 2853 Intent activityIntent = new Intent(); 2854 activityIntent.setClassName(NOTIFICATIONPROVIDER, RICH_NOTIFICATION_ACTIVITY); 2855 activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 2856 activityIntent.putExtra("action", action); 2857 mContext.startActivity(activityIntent); 2858 } 2859 testNotificationUriPermissionsGranted()2860 public void testNotificationUriPermissionsGranted() throws Exception { 2861 Uri background7Uri = Uri.parse( 2862 "content://com.android.test.notificationprovider.provider/background7.png"); 2863 Uri background8Uri = Uri.parse( 2864 "content://com.android.test.notificationprovider.provider/background8.png"); 2865 2866 toggleListenerAccess(true); 2867 Thread.sleep(500); // wait for listener to be allowed 2868 2869 mListener = TestNotificationListener.getInstance(); 2870 assertNotNull(mListener); 2871 2872 try { 2873 // Post #7 2874 performNotificationProviderAction("send-7"); 2875 2876 assertEquals(background7Uri, getNotificationBackgroundImageUri(7)); 2877 assertNotificationCancelled(8, true); 2878 assertAccessible(background7Uri); 2879 assertInaccessible(background8Uri); 2880 2881 // Post #8 2882 performNotificationProviderAction("send-8"); 2883 2884 assertEquals(background7Uri, getNotificationBackgroundImageUri(7)); 2885 assertEquals(background8Uri, getNotificationBackgroundImageUri(8)); 2886 assertAccessible(background7Uri); 2887 assertAccessible(background8Uri); 2888 2889 // Cancel #7 2890 performNotificationProviderAction("cancel-7"); 2891 2892 assertNotificationCancelled(7, true); 2893 assertEquals(background8Uri, getNotificationBackgroundImageUri(8)); 2894 assertInaccessible(background7Uri); 2895 assertAccessible(background8Uri); 2896 2897 // Cancel #8 2898 performNotificationProviderAction("cancel-8"); 2899 2900 assertNotificationCancelled(7, true); 2901 assertNotificationCancelled(8, true); 2902 assertInaccessible(background7Uri); 2903 assertInaccessible(background8Uri); 2904 2905 } finally { 2906 // Clean up -- reset any remaining notifications 2907 performNotificationProviderAction("reset"); 2908 Thread.sleep(500); 2909 } 2910 } 2911 testNotificationUriPermissionsGrantedToNewListeners()2912 public void testNotificationUriPermissionsGrantedToNewListeners() throws Exception { 2913 Uri background7Uri = Uri.parse( 2914 "content://com.android.test.notificationprovider.provider/background7.png"); 2915 2916 try { 2917 // Post #7 2918 performNotificationProviderAction("send-7"); 2919 2920 Thread.sleep(500); 2921 // Don't have access the notification yet, but we can test the URI 2922 assertInaccessible(background7Uri); 2923 2924 toggleListenerAccess(true); 2925 Thread.sleep(500); // wait for listener to be allowed 2926 2927 mListener = TestNotificationListener.getInstance(); 2928 assertNotNull(mListener); 2929 2930 assertEquals(background7Uri, getNotificationBackgroundImageUri(7)); 2931 assertAccessible(background7Uri); 2932 2933 } finally { 2934 // Clean Up -- Cancel #7 2935 performNotificationProviderAction("cancel-7"); 2936 Thread.sleep(500); 2937 } 2938 } 2939 testNotificationUriPermissionsRevokedFromRemovedListeners()2940 public void testNotificationUriPermissionsRevokedFromRemovedListeners() throws Exception { 2941 Uri background7Uri = Uri.parse( 2942 "content://com.android.test.notificationprovider.provider/background7.png"); 2943 2944 toggleListenerAccess(true); 2945 Thread.sleep(500); // wait for listener to be allowed 2946 2947 try { 2948 // Post #7 2949 performNotificationProviderAction("send-7"); 2950 2951 mListener = TestNotificationListener.getInstance(); 2952 assertNotNull(mListener); 2953 2954 assertEquals(background7Uri, getNotificationBackgroundImageUri(7)); 2955 assertAccessible(background7Uri); 2956 2957 // Remove the listener to ensure permissions get revoked 2958 toggleListenerAccess(false); 2959 Thread.sleep(500); // wait for listener to be disabled 2960 2961 assertInaccessible(background7Uri); 2962 2963 } finally { 2964 // Clean Up -- Cancel #7 2965 performNotificationProviderAction("cancel-7"); 2966 Thread.sleep(500); 2967 } 2968 } 2969 2970 private class NotificationListenerConnection implements ServiceConnection { 2971 private final Semaphore mSemaphore = new Semaphore(0); 2972 2973 @Override onServiceConnected(ComponentName className, IBinder service)2974 public void onServiceConnected(ComponentName className, IBinder service) { 2975 mNotificationUriAccessService = INotificationUriAccessService.Stub.asInterface(service); 2976 mSemaphore.release(); 2977 } 2978 2979 @Override onServiceDisconnected(ComponentName className)2980 public void onServiceDisconnected(ComponentName className) { 2981 mNotificationUriAccessService = null; 2982 } 2983 waitForService()2984 public void waitForService() { 2985 try { 2986 if (mSemaphore.tryAcquire(5, TimeUnit.SECONDS)) { 2987 return; 2988 } 2989 } catch (InterruptedException e) { 2990 } 2991 fail("failed to connec to service"); 2992 } 2993 } 2994 testNotificationUriPermissionsRevokedOnlyFromRemovedListeners()2995 public void testNotificationUriPermissionsRevokedOnlyFromRemovedListeners() throws Exception { 2996 Uri background7Uri = Uri.parse( 2997 "content://com.android.test.notificationprovider.provider/background7.png"); 2998 2999 // Connect to a service in the NotificationListener app which allows us to validate URI 3000 // permissions granted to a second app, so that we show that permissions aren't being 3001 // revoked too broadly. 3002 final Intent intent = new Intent(); 3003 intent.setComponent(new ComponentName("com.android.test.notificationlistener", 3004 "com.android.test.notificationlistener.NotificationUriAccessService")); 3005 NotificationListenerConnection connection = new NotificationListenerConnection(); 3006 mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE); 3007 connection.waitForService(); 3008 3009 // Before starting the test, make sure the service works, that there is no listener, and 3010 // that the URI starts inaccessible to that process. 3011 mNotificationUriAccessService.ensureNotificationListenerServiceConnected(false); 3012 assertFalse(mNotificationUriAccessService.isFileUriAccessible(background7Uri)); 3013 3014 // Give the NotificationListener app access to notifications, and validate that. 3015 toggleExternalListenerAccess(new ComponentName("com.android.test.notificationlistener", 3016 "com.android.test.notificationlistener.TestNotificationListener"), true); 3017 Thread.sleep(500); 3018 mNotificationUriAccessService.ensureNotificationListenerServiceConnected(true); 3019 assertFalse(mNotificationUriAccessService.isFileUriAccessible(background7Uri)); 3020 3021 // Give the test app access to notifications, and get that listener 3022 toggleListenerAccess(true); 3023 Thread.sleep(500); // wait for listener to be allowed 3024 mListener = TestNotificationListener.getInstance(); 3025 assertNotNull(mListener); 3026 3027 try { 3028 try { 3029 // Post #7 3030 performNotificationProviderAction("send-7"); 3031 3032 // Check that both the test app (this code) and the external app have URI access. 3033 assertEquals(background7Uri, getNotificationBackgroundImageUri(7)); 3034 assertAccessible(background7Uri); 3035 assertTrue(mNotificationUriAccessService.isFileUriAccessible(background7Uri)); 3036 3037 // Remove the listener to ensure permissions get revoked 3038 toggleListenerAccess(false); 3039 Thread.sleep(500); // wait for listener to be disabled 3040 3041 // Ensure that revoking listener access to this one app does not effect the other. 3042 assertInaccessible(background7Uri); 3043 assertTrue(mNotificationUriAccessService.isFileUriAccessible(background7Uri)); 3044 3045 } finally { 3046 // Clean Up -- Cancel #7 3047 performNotificationProviderAction("cancel-7"); 3048 Thread.sleep(500); 3049 } 3050 3051 // Finally, cancelling the permission must still revoke those other permissions. 3052 assertFalse(mNotificationUriAccessService.isFileUriAccessible(background7Uri)); 3053 3054 } finally { 3055 // Clean Up -- Make sure the external listener is has access revoked 3056 toggleExternalListenerAccess(new ComponentName("com.android.test.notificationlistener", 3057 "com.android.test.notificationlistener.TestNotificationListener"), false); 3058 } 3059 } 3060 assertAccessible(Uri uri)3061 private void assertAccessible(Uri uri) 3062 throws IOException { 3063 ContentResolver contentResolver = mContext.getContentResolver(); 3064 try (AssetFileDescriptor fd = contentResolver.openAssetFile(uri, "r", null)) { 3065 assertNotNull(fd); 3066 } catch (SecurityException e) { 3067 throw new AssertionError("URI should be accessible: " + uri, e); 3068 } 3069 } 3070 assertInaccessible(Uri uri)3071 private void assertInaccessible(Uri uri) 3072 throws IOException { 3073 ContentResolver contentResolver = mContext.getContentResolver(); 3074 try (AssetFileDescriptor fd = contentResolver.openAssetFile(uri, "r", null)) { 3075 fail("URI should be inaccessible: " + uri); 3076 } catch (SecurityException e) { 3077 // pass 3078 } 3079 } 3080 3081 @NonNull getNotificationBackgroundImageUri(int notificationId)3082 private Uri getNotificationBackgroundImageUri(int notificationId) { 3083 StatusBarNotification sbn = findPostedNotification(notificationId, true); 3084 assertNotNull(sbn); 3085 String imageUriString = sbn.getNotification().extras 3086 .getString(Notification.EXTRA_BACKGROUND_IMAGE_URI); 3087 assertNotNull(imageUriString); 3088 return Uri.parse(imageUriString); 3089 } 3090 uncheck(ThrowingSupplier<T> supplier)3091 private <T> T uncheck(ThrowingSupplier<T> supplier) { 3092 try { 3093 return supplier.get(); 3094 } catch (Exception e) { 3095 throw new CompletionException(e); 3096 } 3097 } 3098 testNotificationListener_setNotificationsShown()3099 public void testNotificationListener_setNotificationsShown() throws Exception { 3100 toggleListenerAccess(true); 3101 Thread.sleep(500); // wait for listener to be allowed 3102 3103 mListener = TestNotificationListener.getInstance(); 3104 assertNotNull(mListener); 3105 final int notificationId1 = 1003; 3106 final int notificationId2 = 1004; 3107 3108 sendNotification(notificationId1, R.drawable.black); 3109 sendNotification(notificationId2, R.drawable.black); 3110 Thread.sleep(500); // wait for notification listener to receive notification 3111 3112 StatusBarNotification sbn1 = findPostedNotification(notificationId1, false); 3113 StatusBarNotification sbn2 = findPostedNotification(notificationId2, false); 3114 mListener.setNotificationsShown(new String[]{ sbn1.getKey() }); 3115 3116 toggleListenerAccess(false); 3117 Thread.sleep(500); // wait for listener to be disallowed 3118 try { 3119 mListener.setNotificationsShown(new String[]{ sbn2.getKey() }); 3120 fail("Should not be able to set shown if listener access isn't granted"); 3121 } catch (SecurityException e) { 3122 // expected 3123 } 3124 } 3125 testNotificationListener_getNotificationChannels()3126 public void testNotificationListener_getNotificationChannels() throws Exception { 3127 toggleListenerAccess(true); 3128 Thread.sleep(500); // wait for listener to be allowed 3129 3130 mListener = TestNotificationListener.getInstance(); 3131 assertNotNull(mListener); 3132 3133 try { 3134 mListener.getNotificationChannels(mContext.getPackageName(), UserHandle.CURRENT); 3135 fail("Shouldn't be able get channels without CompanionDeviceManager#getAssociations()"); 3136 } catch (SecurityException e) { 3137 // expected 3138 } 3139 } 3140 testNotificationListener_getNotificationChannelGroups()3141 public void testNotificationListener_getNotificationChannelGroups() throws Exception { 3142 toggleListenerAccess(true); 3143 Thread.sleep(500); // wait for listener to be allowed 3144 3145 mListener = TestNotificationListener.getInstance(); 3146 assertNotNull(mListener); 3147 try { 3148 mListener.getNotificationChannelGroups(mContext.getPackageName(), UserHandle.CURRENT); 3149 fail("Should not be able get groups without CompanionDeviceManager#getAssociations()"); 3150 } catch (SecurityException e) { 3151 // expected 3152 } 3153 } 3154 testNotificationListener_updateNotificationChannel()3155 public void testNotificationListener_updateNotificationChannel() throws Exception { 3156 toggleListenerAccess(true); 3157 Thread.sleep(500); // wait for listener to be allowed 3158 3159 mListener = TestNotificationListener.getInstance(); 3160 assertNotNull(mListener); 3161 3162 NotificationChannel channel = new NotificationChannel( 3163 NOTIFICATION_CHANNEL_ID, "name", IMPORTANCE_DEFAULT); 3164 try { 3165 mListener.updateNotificationChannel(mContext.getPackageName(), UserHandle.CURRENT, 3166 channel); 3167 fail("Shouldn't be able to update channel without " 3168 + "CompanionDeviceManager#getAssociations()"); 3169 } catch (SecurityException e) { 3170 // expected 3171 } 3172 } 3173 testNotificationListener_getActiveNotifications()3174 public void testNotificationListener_getActiveNotifications() throws Exception { 3175 toggleListenerAccess(true); 3176 Thread.sleep(500); // wait for listener to be allowed 3177 3178 mListener = TestNotificationListener.getInstance(); 3179 assertNotNull(mListener); 3180 final int notificationId1 = 1001; 3181 final int notificationId2 = 1002; 3182 3183 sendNotification(notificationId1, R.drawable.black); 3184 sendNotification(notificationId2, R.drawable.black); 3185 Thread.sleep(500); // wait for notification listener to receive notification 3186 3187 StatusBarNotification sbn1 = findPostedNotification(notificationId1, false); 3188 StatusBarNotification sbn2 = findPostedNotification(notificationId2, false); 3189 StatusBarNotification[] notifs = 3190 mListener.getActiveNotifications(new String[]{ sbn2.getKey(), sbn1.getKey() }); 3191 assertEquals(sbn2.getKey(), notifs[0].getKey()); 3192 assertEquals(sbn2.getId(), notifs[0].getId()); 3193 assertEquals(sbn2.getPackageName(), notifs[0].getPackageName()); 3194 3195 assertEquals(sbn1.getKey(), notifs[1].getKey()); 3196 assertEquals(sbn1.getId(), notifs[1].getId()); 3197 assertEquals(sbn1.getPackageName(), notifs[1].getPackageName()); 3198 } 3199 3200 testNotificationListener_getCurrentRanking()3201 public void testNotificationListener_getCurrentRanking() throws Exception { 3202 toggleListenerAccess(true); 3203 Thread.sleep(500); // wait for listener to be allowed 3204 3205 mListener = TestNotificationListener.getInstance(); 3206 assertNotNull(mListener); 3207 3208 sendNotification(1, R.drawable.black); 3209 Thread.sleep(500); // wait for notification listener to receive notification 3210 3211 assertEquals(mListener.mRankingMap, mListener.getCurrentRanking()); 3212 } 3213 testNotificationListener_cancelNotifications()3214 public void testNotificationListener_cancelNotifications() throws Exception { 3215 toggleListenerAccess(true); 3216 Thread.sleep(500); // wait for listener to be allowed 3217 3218 mListener = TestNotificationListener.getInstance(); 3219 assertNotNull(mListener); 3220 final int notificationId = 1006; 3221 3222 sendNotification(notificationId, R.drawable.black); 3223 Thread.sleep(500); // wait for notification listener to receive notification 3224 3225 StatusBarNotification sbn = findPostedNotification(notificationId, false); 3226 3227 mListener.cancelNotification(sbn.getPackageName(), sbn.getTag(), sbn.getId()); 3228 if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) { 3229 if (!checkNotificationExistence(notificationId, /*shouldExist=*/ true)) { 3230 fail("Notification shouldn't have been cancelled. " 3231 + "cancelNotification(String, String, int) shouldn't cancel notif for L+"); 3232 } 3233 } else { 3234 // Tested in LegacyNotificationManager20Test 3235 if (!checkNotificationExistence(notificationId, /*shouldExist=*/ false)) { 3236 fail("Notification should have been cancelled for targetSdk below L. targetSdk=" 3237 + mContext.getApplicationInfo().targetSdkVersion); 3238 } 3239 } 3240 3241 mListener.cancelNotifications(new String[]{ sbn.getKey() }); 3242 if (!checkNotificationExistence(notificationId, /*shouldExist=*/ false)) { 3243 fail("Failed to cancel notification id=" + notificationId); 3244 } 3245 } 3246 testNotificationManagerPolicy_priorityCategoriesToString()3247 public void testNotificationManagerPolicy_priorityCategoriesToString() { 3248 String zeroString = NotificationManager.Policy.priorityCategoriesToString(0); 3249 assertEquals("priorityCategories of 0 produces empty string", "", zeroString); 3250 3251 String oneString = NotificationManager.Policy.priorityCategoriesToString(1); 3252 assertNotNull("priorityCategories of 1 returns a string", oneString); 3253 boolean lengthGreaterThanZero = oneString.length() > 0; 3254 assertTrue("priorityCategories of 1 returns a string with length greater than 0", 3255 lengthGreaterThanZero); 3256 3257 String badNumberString = NotificationManager.Policy.priorityCategoriesToString(1234567); 3258 assertNotNull("priorityCategories with a non-relevant int returns a string", 3259 badNumberString); 3260 } 3261 testNotificationManagerPolicy_prioritySendersToString()3262 public void testNotificationManagerPolicy_prioritySendersToString() { 3263 String zeroString = NotificationManager.Policy.prioritySendersToString(0); 3264 assertNotNull("prioritySenders of 1 returns a string", zeroString); 3265 boolean lengthGreaterThanZero = zeroString.length() > 0; 3266 assertTrue("prioritySenders of 1 returns a string with length greater than 0", 3267 lengthGreaterThanZero); 3268 3269 String badNumberString = NotificationManager.Policy.prioritySendersToString(1234567); 3270 assertNotNull("prioritySenders with a non-relevant int returns a string", badNumberString); 3271 } 3272 testNotificationManagerPolicy_suppressedEffectsToString()3273 public void testNotificationManagerPolicy_suppressedEffectsToString() { 3274 String zeroString = NotificationManager.Policy.suppressedEffectsToString(0); 3275 assertEquals("suppressedEffects of 0 produces empty string", "", zeroString); 3276 3277 String oneString = NotificationManager.Policy.suppressedEffectsToString(1); 3278 assertNotNull("suppressedEffects of 1 returns a string", oneString); 3279 boolean lengthGreaterThanZero = oneString.length() > 0; 3280 assertTrue("suppressedEffects of 1 returns a string with length greater than 0", 3281 lengthGreaterThanZero); 3282 3283 String badNumberString = NotificationManager.Policy.suppressedEffectsToString(1234567); 3284 assertNotNull("suppressedEffects with a non-relevant int returns a string", 3285 badNumberString); 3286 } 3287 testNotificationManagerBubblePolicy_flag_intentBubble()3288 public void testNotificationManagerBubblePolicy_flag_intentBubble() 3289 throws Exception { 3290 if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) { 3291 // These do not support bubbles. 3292 return; 3293 } 3294 try { 3295 setBubblesGlobal(true); 3296 setBubblesAppPref(1 /* all */); 3297 setBubblesChannelAllowed(true); 3298 createDynamicShortcut(); 3299 3300 Notification.Builder nb = getConversationNotification(); 3301 boolean shouldBeBubble = !mActivityManager.isLowRamDevice(); 3302 sendAndVerifyBubble(1, nb, null /* use default metadata */, shouldBeBubble); 3303 } finally { 3304 deleteShortcuts(); 3305 } 3306 } 3307 testNotificationManagerBubblePolicy_noFlag_service()3308 public void testNotificationManagerBubblePolicy_noFlag_service() 3309 throws Exception { 3310 if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) { 3311 // These do not support bubbles. 3312 return; 3313 } 3314 Intent serviceIntent = new Intent(mContext, BubblesTestService.class); 3315 serviceIntent.putExtra(EXTRA_TEST_CASE, TEST_MESSAGING); 3316 try { 3317 setBubblesGlobal(true); 3318 setBubblesAppPref(1 /* all */); 3319 setBubblesChannelAllowed(true); 3320 3321 createDynamicShortcut(); 3322 setUpNotifListener(); 3323 3324 mContext.startService(serviceIntent); 3325 3326 // No services in R (allowed in Q) 3327 verifyNotificationBubbleState(BUBBLE_NOTIF_ID, false /* shouldBeBubble */); 3328 } finally { 3329 deleteShortcuts(); 3330 mContext.stopService(serviceIntent); 3331 } 3332 } 3333 testNotificationManagerBubblePolicy_noFlag_phonecall()3334 public void testNotificationManagerBubblePolicy_noFlag_phonecall() 3335 throws Exception { 3336 if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) { 3337 // These do not support bubbles. 3338 return; 3339 } 3340 Intent serviceIntent = new Intent(mContext, BubblesTestService.class); 3341 serviceIntent.putExtra(EXTRA_TEST_CASE, TEST_CALL); 3342 3343 try { 3344 setBubblesGlobal(true); 3345 setBubblesAppPref(1 /* all */); 3346 setBubblesChannelAllowed(true); 3347 3348 createDynamicShortcut(); 3349 setUpNotifListener(); 3350 3351 mContext.startService(serviceIntent); 3352 3353 // No phonecalls in R (allowed in Q) 3354 verifyNotificationBubbleState(BUBBLE_NOTIF_ID, false /* shouldBeBubble */); 3355 } finally { 3356 deleteShortcuts(); 3357 mContext.stopService(serviceIntent); 3358 } 3359 } 3360 testNotificationManagerBubblePolicy_noFlag_foreground()3361 public void testNotificationManagerBubblePolicy_noFlag_foreground() throws Exception { 3362 if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) { 3363 // These do not support bubbles. 3364 return; 3365 } 3366 try { 3367 setBubblesGlobal(true); 3368 setBubblesAppPref(1 /* all */); 3369 setBubblesChannelAllowed(true); 3370 3371 createDynamicShortcut(); 3372 setUpNotifListener(); 3373 3374 // Start & get the activity 3375 SendBubbleActivity a = startSendBubbleActivity(); 3376 // Send a bubble that doesn't fulfill policy from foreground 3377 a.sendInvalidBubble(BUBBLE_NOTIF_ID, false /* autoExpand */); 3378 3379 // No foreground bubbles that don't fulfill policy in R (allowed in Q) 3380 verifyNotificationBubbleState(BUBBLE_NOTIF_ID, false /* shouldBeBubble */); 3381 } finally { 3382 deleteShortcuts(); 3383 cleanupSendBubbleActivity(); 3384 } 3385 } 3386 testNotificationManagerBubble_checkActivityFlagsDocumentLaunchMode()3387 public void testNotificationManagerBubble_checkActivityFlagsDocumentLaunchMode() 3388 throws Exception { 3389 if (FeatureUtil.isAutomotive() || FeatureUtil.isTV() 3390 || mActivityManager.isLowRamDevice()) { 3391 // These do not support bubbles. 3392 return; 3393 } 3394 try { 3395 setBubblesGlobal(true); 3396 setBubblesAppPref(1 /* all */); 3397 setBubblesChannelAllowed(true); 3398 3399 createDynamicShortcut(); 3400 setUpNotifListener(); 3401 3402 // make ourselves foreground so we can auto-expand the bubble & check the intent flags 3403 SendBubbleActivity a = startSendBubbleActivity(); 3404 3405 // Prep to find bubbled activity 3406 Class clazz = BubbledActivity.class; 3407 Instrumentation.ActivityResult result = 3408 new Instrumentation.ActivityResult(0, new Intent()); 3409 Instrumentation.ActivityMonitor monitor = 3410 new Instrumentation.ActivityMonitor(clazz.getName(), result, false); 3411 InstrumentationRegistry.getInstrumentation().addMonitor(monitor); 3412 3413 a.sendBubble(BUBBLE_NOTIF_ID, true /* autoExpand */, false /* suppressNotif */); 3414 3415 verifyNotificationBubbleState(BUBBLE_NOTIF_ID, true /* shouldBeBubble */); 3416 3417 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 3418 3419 BubbledActivity activity = (BubbledActivity) monitor.waitForActivityWithTimeout( 3420 ACTIVITY_LAUNCH_TIMEOUT); 3421 assertNotNull(String.format( 3422 "Failed to detect BubbleActivity after %d ms", ACTIVITY_LAUNCH_TIMEOUT), activity); 3423 assertTrue((activity.getIntent().getFlags() & FLAG_ACTIVITY_NEW_DOCUMENT) != 0); 3424 assertTrue((activity.getIntent().getFlags() & FLAG_ACTIVITY_MULTIPLE_TASK) != 0); 3425 } finally { 3426 deleteShortcuts(); 3427 cleanupSendBubbleActivity(); 3428 } 3429 } 3430 testNotificationManagerBubblePolicy_flag_shortcutBubble()3431 public void testNotificationManagerBubblePolicy_flag_shortcutBubble() 3432 throws Exception { 3433 if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) { 3434 // These do not support bubbles. 3435 return; 3436 } 3437 try { 3438 setBubblesGlobal(true); 3439 setBubblesAppPref(1 /* all */); 3440 setBubblesChannelAllowed(true); 3441 3442 createDynamicShortcut(); 3443 3444 Notification.Builder nb = getConversationNotification(); 3445 Notification.BubbleMetadata data = 3446 new Notification.BubbleMetadata.Builder(SHARE_SHORTCUT_ID) 3447 .build(); 3448 3449 boolean shouldBeBubble = !mActivityManager.isLowRamDevice(); 3450 sendAndVerifyBubble(1, nb, data, shouldBeBubble); 3451 } finally { 3452 deleteShortcuts(); 3453 } 3454 } 3455 testNotificationManagerBubblePolicy_noFlag_invalidShortcut()3456 public void testNotificationManagerBubblePolicy_noFlag_invalidShortcut() 3457 throws Exception { 3458 if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) { 3459 // These do not support bubbles. 3460 return; 3461 } 3462 try { 3463 setBubblesGlobal(true); 3464 setBubblesAppPref(1 /* all */); 3465 setBubblesChannelAllowed(true); 3466 3467 createDynamicShortcut(); 3468 3469 Notification.Builder nb = getConversationNotification(); 3470 nb.setShortcutId("invalid"); 3471 Notification.BubbleMetadata data = 3472 new Notification.BubbleMetadata.Builder("invalid") 3473 .build(); 3474 3475 sendAndVerifyBubble(1, nb, data, false); 3476 } finally { 3477 deleteShortcuts(); 3478 } 3479 } 3480 testNotificationManagerBubblePolicy_noFlag_invalidNotif()3481 public void testNotificationManagerBubblePolicy_noFlag_invalidNotif() 3482 throws Exception { 3483 if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) { 3484 // These do not support bubbles. 3485 return; 3486 } 3487 try { 3488 setBubblesGlobal(true); 3489 setBubblesAppPref(1 /* all */); 3490 setBubblesChannelAllowed(true); 3491 3492 createDynamicShortcut(); 3493 3494 Notification.BubbleMetadata data = 3495 new Notification.BubbleMetadata.Builder(SHARE_SHORTCUT_ID) 3496 .build(); 3497 3498 sendAndVerifyBubble(1, null /* use default notif builder */, data, 3499 false /* shouldBeBubble */); 3500 } finally { 3501 deleteShortcuts(); 3502 } 3503 } 3504 testNotificationManagerBubblePolicy_appAll_globalOn()3505 public void testNotificationManagerBubblePolicy_appAll_globalOn() throws Exception { 3506 if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) { 3507 // These do not support bubbles. 3508 return; 3509 } 3510 try { 3511 setBubblesGlobal(true); 3512 setBubblesAppPref(1 /* all */); 3513 setBubblesChannelAllowed(true); 3514 3515 createDynamicShortcut(); 3516 Notification.BubbleMetadata data = 3517 new Notification.BubbleMetadata.Builder(SHARE_SHORTCUT_ID) 3518 .build(); 3519 Notification.Builder nb = getConversationNotification(); 3520 3521 boolean shouldBeBubble = !mActivityManager.isLowRamDevice(); 3522 sendAndVerifyBubble(1, nb, data, shouldBeBubble); 3523 } finally { 3524 deleteShortcuts(); 3525 } 3526 } 3527 testNotificationManagerBubblePolicy_appAll_globalOff()3528 public void testNotificationManagerBubblePolicy_appAll_globalOff() throws Exception { 3529 if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) { 3530 // These do not support bubbles. 3531 return; 3532 } 3533 try { 3534 setBubblesGlobal(false); 3535 setBubblesAppPref(1 /* all */); 3536 setBubblesChannelAllowed(true); 3537 3538 createDynamicShortcut(); 3539 Notification.BubbleMetadata data = 3540 new Notification.BubbleMetadata.Builder(SHARE_SHORTCUT_ID) 3541 .build(); 3542 Notification.Builder nb = getConversationNotification(); 3543 3544 sendAndVerifyBubble(1, nb, data, false); 3545 } finally { 3546 deleteShortcuts(); 3547 } 3548 } 3549 testNotificationManagerBubblePolicy_appAll_channelNo()3550 public void testNotificationManagerBubblePolicy_appAll_channelNo() throws Exception { 3551 if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) { 3552 // These do not support bubbles. 3553 return; 3554 } 3555 try { 3556 setBubblesGlobal(true); 3557 setBubblesAppPref(1 /* all */); 3558 setBubblesChannelAllowed(false); 3559 3560 createDynamicShortcut(); 3561 Notification.BubbleMetadata data = 3562 new Notification.BubbleMetadata.Builder(SHARE_SHORTCUT_ID) 3563 .build(); 3564 Notification.Builder nb = getConversationNotification(); 3565 3566 sendAndVerifyBubble(1, nb, data, false); 3567 } finally { 3568 deleteShortcuts(); 3569 } 3570 } 3571 testNotificationManagerBubblePolicy_appSelected_channelNo()3572 public void testNotificationManagerBubblePolicy_appSelected_channelNo() throws Exception { 3573 if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) { 3574 // These do not support bubbles. 3575 return; 3576 } 3577 try { 3578 setBubblesGlobal(true); 3579 setBubblesAppPref(2 /* selected */); 3580 setBubblesChannelAllowed(false); 3581 3582 createDynamicShortcut(); 3583 Notification.BubbleMetadata data = 3584 new Notification.BubbleMetadata.Builder(SHARE_SHORTCUT_ID) 3585 .build(); 3586 Notification.Builder nb = getConversationNotification(); 3587 3588 sendAndVerifyBubble(1, nb, data, false); 3589 } finally { 3590 deleteShortcuts(); 3591 } 3592 } 3593 testNotificationManagerBubblePolicy_appSelected_channelYes()3594 public void testNotificationManagerBubblePolicy_appSelected_channelYes() throws Exception { 3595 if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) { 3596 // These do not support bubbles. 3597 return; 3598 } 3599 try { 3600 setBubblesGlobal(true); 3601 setBubblesAppPref(2 /* selected */); 3602 setBubblesChannelAllowed(true); 3603 3604 createDynamicShortcut(); 3605 Notification.BubbleMetadata data = 3606 new Notification.BubbleMetadata.Builder(SHARE_SHORTCUT_ID) 3607 .build(); 3608 Notification.Builder nb = getConversationNotification(); 3609 3610 boolean shouldBeBubble = !mActivityManager.isLowRamDevice(); 3611 sendAndVerifyBubble(1, nb, data, shouldBeBubble); 3612 } finally { 3613 deleteShortcuts(); 3614 } 3615 } 3616 testNotificationManagerBubblePolicy_appNone_channelNo()3617 public void testNotificationManagerBubblePolicy_appNone_channelNo() throws Exception { 3618 if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) { 3619 // These do not support bubbles. 3620 return; 3621 } 3622 try { 3623 setBubblesGlobal(true); 3624 setBubblesAppPref(0 /* none */); 3625 setBubblesChannelAllowed(false); 3626 3627 createDynamicShortcut(); 3628 Notification.BubbleMetadata data = 3629 new Notification.BubbleMetadata.Builder(SHARE_SHORTCUT_ID) 3630 .build(); 3631 Notification.Builder nb = getConversationNotification(); 3632 3633 sendAndVerifyBubble(1, nb, data, false); 3634 } finally { 3635 deleteShortcuts(); 3636 } 3637 } 3638 testNotificationManagerBubblePolicy_noFlag_shortcutRemoved()3639 public void testNotificationManagerBubblePolicy_noFlag_shortcutRemoved() 3640 throws Exception { 3641 if (FeatureUtil.isAutomotive() || FeatureUtil.isTV() 3642 || mActivityManager.isLowRamDevice()) { 3643 // These do not support bubbles. 3644 return; 3645 } 3646 3647 try { 3648 setBubblesGlobal(true); 3649 setBubblesAppPref(1 /* all */); 3650 setBubblesChannelAllowed(true); 3651 createDynamicShortcut(); 3652 Notification.BubbleMetadata data = 3653 new Notification.BubbleMetadata.Builder(SHARE_SHORTCUT_ID) 3654 .build(); 3655 Notification.Builder nb = getConversationNotification(); 3656 3657 sendAndVerifyBubble(42, nb, data, true /* shouldBeBubble */); 3658 mListener.resetData(); 3659 3660 deleteShortcuts(); 3661 verifyNotificationBubbleState(42, false /* should be bubble */); 3662 } finally { 3663 deleteShortcuts(); 3664 } 3665 } 3666 testNotificationManagerBubbleNotificationSuppression()3667 public void testNotificationManagerBubbleNotificationSuppression() throws Exception { 3668 if (FeatureUtil.isAutomotive() || FeatureUtil.isTV() 3669 || mActivityManager.isLowRamDevice()) { 3670 // These do not support bubbles. 3671 return; 3672 } 3673 try { 3674 setBubblesGlobal(true); 3675 setBubblesAppPref(1 /* all */); 3676 setBubblesChannelAllowed(true); 3677 3678 createDynamicShortcut(); 3679 setUpNotifListener(); 3680 3681 // make ourselves foreground so we can specify suppress notification flag 3682 SendBubbleActivity a = startSendBubbleActivity(); 3683 3684 // send the bubble with notification suppressed 3685 a.sendBubble(BUBBLE_NOTIF_ID, false /* autoExpand */, true /* suppressNotif */); 3686 verifyNotificationBubbleState(BUBBLE_NOTIF_ID, true /* shouldBeBubble */); 3687 3688 // check for the notification 3689 StatusBarNotification sbnSuppressed = mListener.mPosted.get(0); 3690 assertNotNull(sbnSuppressed); 3691 // check for suppression state 3692 Notification.BubbleMetadata metadata = 3693 sbnSuppressed.getNotification().getBubbleMetadata(); 3694 assertNotNull(metadata); 3695 assertTrue(metadata.isNotificationSuppressed()); 3696 3697 mListener.resetData(); 3698 3699 // send the bubble with notification NOT suppressed 3700 a.sendBubble(BUBBLE_NOTIF_ID, false /* autoExpand */, false /* suppressNotif */); 3701 verifyNotificationBubbleState(BUBBLE_NOTIF_ID, true /* shouldBubble */); 3702 3703 // check for the notification 3704 StatusBarNotification sbnNotSuppressed = mListener.mPosted.get(0); 3705 assertNotNull(sbnNotSuppressed); 3706 // check for suppression state 3707 metadata = sbnNotSuppressed.getNotification().getBubbleMetadata(); 3708 assertNotNull(metadata); 3709 assertFalse(metadata.isNotificationSuppressed()); 3710 } finally { 3711 cleanupSendBubbleActivity(); 3712 deleteShortcuts(); 3713 } 3714 } 3715 testNotificationManagerBubble_checkIsBubbled_pendingIntent()3716 public void testNotificationManagerBubble_checkIsBubbled_pendingIntent() 3717 throws Exception { 3718 if (FeatureUtil.isAutomotive() || FeatureUtil.isTV() 3719 || mActivityManager.isLowRamDevice()) { 3720 // These do not support bubbles. 3721 return; 3722 } 3723 try { 3724 setBubblesGlobal(true); 3725 setBubblesAppPref(1 /* all */); 3726 setBubblesChannelAllowed(true); 3727 3728 createDynamicShortcut(); 3729 setUpNotifListener(); 3730 3731 SendBubbleActivity a = startSendBubbleActivity(); 3732 3733 // Prep to find bubbled activity 3734 Class clazz = BubbledActivity.class; 3735 Instrumentation.ActivityResult result = 3736 new Instrumentation.ActivityResult(0, new Intent()); 3737 Instrumentation.ActivityMonitor monitor = 3738 new Instrumentation.ActivityMonitor(clazz.getName(), result, false); 3739 InstrumentationRegistry.getInstrumentation().addMonitor(monitor); 3740 3741 a.sendBubble(BUBBLE_NOTIF_ID, true /* autoExpand */, false /* suppressNotif */); 3742 3743 verifyNotificationBubbleState(BUBBLE_NOTIF_ID, true /* shouldBeBubble */); 3744 3745 BubbledActivity activity = (BubbledActivity) monitor.waitForActivity(); 3746 assertTrue(activity.isLaunchedFromBubble()); 3747 } finally { 3748 deleteShortcuts(); 3749 cleanupSendBubbleActivity(); 3750 } 3751 } 3752 testNotificationManagerBubble_checkIsBubbled_shortcut()3753 public void testNotificationManagerBubble_checkIsBubbled_shortcut() 3754 throws Exception { 3755 if (FeatureUtil.isAutomotive() || FeatureUtil.isTV() 3756 || mActivityManager.isLowRamDevice()) { 3757 // These do not support bubbles. 3758 return; 3759 } 3760 try { 3761 setBubblesGlobal(true); 3762 setBubblesAppPref(1 /* all */); 3763 setBubblesChannelAllowed(true); 3764 3765 createDynamicShortcut(); 3766 setUpNotifListener(); 3767 3768 SendBubbleActivity a = startSendBubbleActivity(); 3769 3770 // Prep to find bubbled activity 3771 Class clazz = BubbledActivity.class; 3772 Instrumentation.ActivityResult result = 3773 new Instrumentation.ActivityResult(0, new Intent()); 3774 Instrumentation.ActivityMonitor monitor = 3775 new Instrumentation.ActivityMonitor(clazz.getName(), result, false); 3776 InstrumentationRegistry.getInstrumentation().addMonitor(monitor); 3777 3778 a.sendBubble(BUBBLE_NOTIF_ID, true /* autoExpand */, 3779 false /* suppressNotif */, 3780 false /* suppressBubble */, 3781 true /* useShortcut */, 3782 true /* setLocus */); 3783 3784 verifyNotificationBubbleState(BUBBLE_NOTIF_ID, true /* shouldBeBubble */); 3785 3786 BubbledActivity activity = (BubbledActivity) monitor.waitForActivity(); 3787 assertTrue(activity.isLaunchedFromBubble()); 3788 } finally { 3789 deleteShortcuts(); 3790 cleanupSendBubbleActivity(); 3791 } 3792 } 3793 3794 /** Verifies the bubble is suppressed when it should be. */ testNotificationManagerBubble_setSuppressBubble()3795 public void testNotificationManagerBubble_setSuppressBubble() 3796 throws Exception { 3797 if (FeatureUtil.isAutomotive() || FeatureUtil.isTV() 3798 || mActivityManager.isLowRamDevice()) { 3799 // These do not support bubbles. 3800 return; 3801 } 3802 try { 3803 setBubblesGlobal(true); 3804 setBubblesAppPref(1 /* all */); 3805 setBubblesChannelAllowed(true); 3806 3807 createDynamicShortcut(); 3808 setUpNotifListener(); 3809 3810 final int notifId = 3; 3811 3812 // Make a bubble 3813 SendBubbleActivity a = startSendBubbleActivity(); 3814 a.sendBubble(notifId, 3815 false /* autoExpand */, 3816 false /* suppressNotif */, 3817 true /* suppressBubble */); 3818 3819 verifyNotificationBubbleState(notifId, true /* shouldBeBubble */); 3820 mListener.resetData(); 3821 3822 // Prep to find bubbled activity 3823 Class clazz = BubbledActivity.class; 3824 Instrumentation.ActivityResult result = 3825 new Instrumentation.ActivityResult(0, new Intent()); 3826 Instrumentation.ActivityMonitor monitor = 3827 new Instrumentation.ActivityMonitor(clazz.getName(), result, false); 3828 InstrumentationRegistry.getInstrumentation().addMonitor(monitor); 3829 3830 // Launch same activity as whats in the bubble 3831 a.startBubbleActivity(notifId); 3832 BubbledActivity activity = (BubbledActivity) monitor.waitForActivity(); 3833 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 3834 3835 // It should have the locusId 3836 assertEquals(new LocusId(String.valueOf(notifId)), 3837 activity.getLocusId()); 3838 3839 // notif gets posted with update, so wait 3840 verifyNotificationBubbleState(notifId, true /* shouldBeBubble */); 3841 mListener.resetData(); 3842 3843 // Bubble should have suppressed flag 3844 StatusBarNotification sbn = findPostedNotification(notifId, true); 3845 assertTrue(sbn.getNotification().getBubbleMetadata().isBubbleSuppressable()); 3846 assertTrue(sbn.getNotification().getBubbleMetadata().isBubbleSuppressed()); 3847 } finally { 3848 deleteShortcuts(); 3849 cleanupSendBubbleActivity(); 3850 } 3851 } 3852 3853 /** Verifies the bubble is not suppressed if dev didn't specify suppressable */ testNotificationManagerBubble_setSuppressBubble_notSuppressable()3854 public void testNotificationManagerBubble_setSuppressBubble_notSuppressable() 3855 throws Exception { 3856 if (FeatureUtil.isAutomotive() || FeatureUtil.isTV() 3857 || mActivityManager.isLowRamDevice()) { 3858 // These do not support bubbles. 3859 return; 3860 } 3861 try { 3862 setBubblesGlobal(true); 3863 setBubblesAppPref(1 /* all */); 3864 setBubblesChannelAllowed(true); 3865 3866 createDynamicShortcut(); 3867 setUpNotifListener(); 3868 3869 // Make a bubble 3870 SendBubbleActivity a = startSendBubbleActivity(); 3871 a.sendBubble(BUBBLE_NOTIF_ID, 3872 false /* autoExpand */, 3873 false /* suppressNotif */, 3874 false /* suppressBubble */); 3875 3876 verifyNotificationBubbleState(BUBBLE_NOTIF_ID, true /* shouldBeBubble */); 3877 mListener.resetData(); 3878 3879 // Prep to find bubbled activity 3880 Class clazz = BubbledActivity.class; 3881 Instrumentation.ActivityResult result = 3882 new Instrumentation.ActivityResult(0, new Intent()); 3883 Instrumentation.ActivityMonitor monitor = 3884 new Instrumentation.ActivityMonitor(clazz.getName(), result, false); 3885 InstrumentationRegistry.getInstrumentation().addMonitor(monitor); 3886 3887 // Launch same activity as whats in the bubble 3888 a.startBubbleActivity(BUBBLE_NOTIF_ID); 3889 BubbledActivity activity = (BubbledActivity) monitor.waitForActivity(); 3890 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 3891 3892 // It should have the locusId 3893 assertEquals(new LocusId(String.valueOf(BUBBLE_NOTIF_ID)), 3894 activity.getLocusId()); 3895 3896 // Wait a little (if it wrongly updates it'd be a new post so wait for that)) 3897 try { 3898 Thread.sleep(500); 3899 } catch (InterruptedException ex) { 3900 } 3901 assertTrue(mListener.mPosted.isEmpty()); 3902 3903 // Bubble should not be suppressed 3904 StatusBarNotification sbn = findPostedNotification(BUBBLE_NOTIF_ID, true); 3905 assertFalse(sbn.getNotification().getBubbleMetadata().isBubbleSuppressable()); 3906 assertFalse(sbn.getNotification().getBubbleMetadata().isBubbleSuppressed()); 3907 } finally { 3908 deleteShortcuts(); 3909 cleanupSendBubbleActivity(); 3910 } 3911 } 3912 3913 /** Verifies the bubble is not suppressed if the activity doesn't have a locusId. */ testNotificationManagerBubble_setSuppressBubble_activityNoLocusId()3914 public void testNotificationManagerBubble_setSuppressBubble_activityNoLocusId() 3915 throws Exception { 3916 if (FeatureUtil.isAutomotive() || FeatureUtil.isTV() 3917 || mActivityManager.isLowRamDevice()) { 3918 // These do not support bubbles. 3919 return; 3920 } 3921 try { 3922 setBubblesGlobal(true); 3923 setBubblesAppPref(1 /* all */); 3924 setBubblesChannelAllowed(true); 3925 3926 createDynamicShortcut(); 3927 setUpNotifListener(); 3928 3929 // Make a bubble 3930 SendBubbleActivity a = startSendBubbleActivity(); 3931 a.sendBubble(BUBBLE_NOTIF_ID, 3932 false /* autoExpand */, 3933 false /* suppressNotif */, 3934 true /* suppressBubble */); 3935 3936 verifyNotificationBubbleState(BUBBLE_NOTIF_ID, true /* shouldBeBubble */); 3937 mListener.resetData(); 3938 3939 // Prep to find bubbled activity 3940 Class clazz = BubbledActivity.class; 3941 Instrumentation.ActivityResult result = 3942 new Instrumentation.ActivityResult(0, new Intent()); 3943 Instrumentation.ActivityMonitor monitor = 3944 new Instrumentation.ActivityMonitor(clazz.getName(), result, false); 3945 InstrumentationRegistry.getInstrumentation().addMonitor(monitor); 3946 3947 // Launch same activity as whats in the bubble 3948 a.startBubbleActivity(BUBBLE_NOTIF_ID, false /* addLocusId */); 3949 BubbledActivity activity = (BubbledActivity) monitor.waitForActivity(); 3950 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 3951 3952 // It shouldn't have the locusId 3953 assertNull(activity.getLocusId()); 3954 3955 // Wait a little (if it wrongly updates it'd be a new post so wait for that)) 3956 try { 3957 Thread.sleep(500); 3958 } catch (InterruptedException ex) { 3959 } 3960 assertTrue(mListener.mPosted.isEmpty()); 3961 3962 // Bubble should not be suppressed 3963 StatusBarNotification sbn = findPostedNotification(BUBBLE_NOTIF_ID, true); 3964 assertTrue(sbn.getNotification().getBubbleMetadata().isBubbleSuppressable()); 3965 assertFalse(sbn.getNotification().getBubbleMetadata().isBubbleSuppressed()); 3966 } finally { 3967 deleteShortcuts(); 3968 cleanupSendBubbleActivity(); 3969 } 3970 } 3971 3972 /** Verifies the bubble is not suppressed if the notification doesn't have a locusId. */ testNotificationManagerBubble_setSuppressBubble_notificationNoLocusId()3973 public void testNotificationManagerBubble_setSuppressBubble_notificationNoLocusId() 3974 throws Exception { 3975 if (FeatureUtil.isAutomotive() || FeatureUtil.isTV() 3976 || mActivityManager.isLowRamDevice()) { 3977 // These do not support bubbles. 3978 return; 3979 } 3980 try { 3981 setBubblesGlobal(true); 3982 setBubblesAppPref(1 /* all */); 3983 setBubblesChannelAllowed(true); 3984 3985 createDynamicShortcut(); 3986 setUpNotifListener(); 3987 3988 // Make a bubble 3989 SendBubbleActivity a = startSendBubbleActivity(); 3990 a.sendBubble(BUBBLE_NOTIF_ID, 3991 false /* autoExpand */, 3992 false /* suppressNotif */, 3993 true /* suppressBubble */, 3994 false /* useShortcut */, 3995 false /* setLocusId */); 3996 3997 verifyNotificationBubbleState(BUBBLE_NOTIF_ID, true /* shouldBeBubble */); 3998 mListener.resetData(); 3999 4000 // Prep to find bubbled activity 4001 Class clazz = BubbledActivity.class; 4002 Instrumentation.ActivityResult result = 4003 new Instrumentation.ActivityResult(0, new Intent()); 4004 Instrumentation.ActivityMonitor monitor = 4005 new Instrumentation.ActivityMonitor(clazz.getName(), result, false); 4006 InstrumentationRegistry.getInstrumentation().addMonitor(monitor); 4007 4008 // Launch same activity as whats in the bubble 4009 a.startBubbleActivity(BUBBLE_NOTIF_ID, true /* addLocusId */); 4010 BubbledActivity activity = (BubbledActivity) monitor.waitForActivity(); 4011 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 4012 4013 // Activity has the locus 4014 assertNotNull(activity.getLocusId()); 4015 4016 // Wait a little (if it wrongly updates it'd be a new post so wait for that)) 4017 try { 4018 Thread.sleep(500); 4019 } catch (InterruptedException ex) { 4020 } 4021 assertTrue(mListener.mPosted.isEmpty()); 4022 4023 // Bubble should not be suppressed & not have a locusId 4024 StatusBarNotification sbn = findPostedNotification(BUBBLE_NOTIF_ID, true); 4025 assertNull(sbn.getNotification().getLocusId()); 4026 assertTrue(sbn.getNotification().getBubbleMetadata().isBubbleSuppressable()); 4027 assertFalse(sbn.getNotification().getBubbleMetadata().isBubbleSuppressed()); 4028 } finally { 4029 deleteShortcuts(); 4030 cleanupSendBubbleActivity(); 4031 } 4032 } 4033 4034 /** Verifies the bubble is unsuppressed when the locus activity is hidden. */ testNotificationManagerBubble_setSuppressBubble_dismissLocusActivity()4035 public void testNotificationManagerBubble_setSuppressBubble_dismissLocusActivity() 4036 throws Exception { 4037 if (FeatureUtil.isAutomotive() || FeatureUtil.isTV() 4038 || mActivityManager.isLowRamDevice()) { 4039 // These do not support bubbles. 4040 return; 4041 } 4042 try { 4043 setBubblesGlobal(true); 4044 setBubblesAppPref(1 /* all */); 4045 setBubblesChannelAllowed(true); 4046 4047 createDynamicShortcut(); 4048 setUpNotifListener(); 4049 4050 final int notifId = 2; 4051 4052 // Make a bubble 4053 SendBubbleActivity a = startSendBubbleActivity(); 4054 a.sendBubble(notifId, 4055 false /* autoExpand */, 4056 false /* suppressNotif */, 4057 true /* suppressBubble */); 4058 4059 verifyNotificationBubbleState(notifId, true); 4060 mListener.resetData(); 4061 4062 StatusBarNotification sbn = findPostedNotification(notifId, true); 4063 assertTrue(sbn.getNotification().getBubbleMetadata().isBubbleSuppressable()); 4064 assertFalse(sbn.getNotification().getBubbleMetadata().isBubbleSuppressed()); 4065 4066 // Prep to find bubbled activity 4067 Class clazz = BubbledActivity.class; 4068 Instrumentation.ActivityResult result = 4069 new Instrumentation.ActivityResult(0, new Intent()); 4070 Instrumentation.ActivityMonitor monitor = 4071 new Instrumentation.ActivityMonitor(clazz.getName(), result, false); 4072 InstrumentationRegistry.getInstrumentation().addMonitor(monitor); 4073 4074 // Launch same activity as whats in the bubble 4075 a.startBubbleActivity(notifId); 4076 BubbledActivity activity = (BubbledActivity) monitor.waitForActivity(); 4077 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 4078 4079 // It should have the locusId 4080 assertEquals(new LocusId(String.valueOf(notifId)), 4081 activity.getLocusId()); 4082 4083 // notif gets posted with update, so wait 4084 verifyNotificationBubbleState(notifId, true /* shouldBeBubble */); 4085 mListener.resetData(); 4086 4087 // Bubble should have suppressed flag 4088 sbn = findPostedNotification(notifId, true); 4089 assertTrue(sbn.getNotification().getBubbleMetadata().isBubbleSuppressable()); 4090 assertTrue(sbn.getNotification().getBubbleMetadata().isBubbleSuppressed()); 4091 4092 activity.finish(); 4093 4094 // notif gets posted with update, so wait 4095 verifyNotificationBubbleState(notifId, true /* shouldBeBubble */); 4096 mListener.resetData(); 4097 4098 sbn = findPostedNotification(notifId, true); 4099 assertTrue(sbn.getNotification().getBubbleMetadata().isBubbleSuppressable()); 4100 assertFalse(sbn.getNotification().getBubbleMetadata().isBubbleSuppressed()); 4101 } finally { 4102 deleteShortcuts(); 4103 cleanupSendBubbleActivity(); 4104 } 4105 } 4106 4107 /** Verifies that a regular activity can't specify a bubble in ActivityOptions */ testNotificationManagerBubble_launchBubble_activityOptions_fails()4108 public void testNotificationManagerBubble_launchBubble_activityOptions_fails() 4109 throws Exception { 4110 try { 4111 // Start test activity 4112 SendBubbleActivity activity = startSendBubbleActivity(); 4113 assertFalse(activity.isLaunchedFromBubble()); 4114 4115 // Should have exception 4116 assertThrows(SecurityException.class, () -> { 4117 Intent i = new Intent(mContext, BubbledActivity.class); 4118 ActivityOptions options = ActivityOptions.makeBasic(); 4119 Bundle b = options.toBundle(); 4120 b.putBoolean("android.activity.launchTypeBubble", true); 4121 activity.startActivity(i, b); 4122 }); 4123 } finally { 4124 cleanupSendBubbleActivity(); 4125 } 4126 } 4127 testOriginalChannelImportance()4128 public void testOriginalChannelImportance() { 4129 NotificationChannel channel = new NotificationChannel(mId, "my channel", IMPORTANCE_HIGH); 4130 4131 mNotificationManager.createNotificationChannel(channel); 4132 4133 NotificationChannel actual = mNotificationManager.getNotificationChannel(channel.getId()); 4134 assertEquals(IMPORTANCE_HIGH, actual.getImportance()); 4135 assertEquals(IMPORTANCE_HIGH, actual.getOriginalImportance()); 4136 4137 // Apps are allowed to downgrade channel importance if the user has not changed any 4138 // fields on this channel yet. 4139 channel.setImportance(IMPORTANCE_DEFAULT); 4140 mNotificationManager.createNotificationChannel(channel); 4141 4142 actual = mNotificationManager.getNotificationChannel(channel.getId()); 4143 assertEquals(IMPORTANCE_DEFAULT, actual.getImportance()); 4144 assertEquals(IMPORTANCE_HIGH, actual.getOriginalImportance()); 4145 } 4146 testCreateConversationChannel()4147 public void testCreateConversationChannel() { 4148 final NotificationChannel channel = 4149 new NotificationChannel(mId, "Messages", IMPORTANCE_DEFAULT); 4150 4151 String conversationId = "person a"; 4152 4153 final NotificationChannel conversationChannel = 4154 new NotificationChannel(mId + "child", 4155 "Messages from " + conversationId, IMPORTANCE_DEFAULT); 4156 conversationChannel.setConversationId(channel.getId(), conversationId); 4157 4158 mNotificationManager.createNotificationChannel(channel); 4159 mNotificationManager.createNotificationChannel(conversationChannel); 4160 4161 compareChannels(conversationChannel, 4162 mNotificationManager.getNotificationChannel(channel.getId(), conversationId)); 4163 } 4164 testConversationRankingFields()4165 public void testConversationRankingFields() throws Exception { 4166 toggleListenerAccess(true); 4167 Thread.sleep(500); // wait for listener to be allowed 4168 4169 mListener = TestNotificationListener.getInstance(); 4170 assertNotNull(mListener); 4171 4172 createDynamicShortcut(); 4173 mNotificationManager.notify(177, getConversationNotification().build()); 4174 4175 if (!checkNotificationExistence(177, /*shouldExist=*/ true)) { 4176 fail("couldn't find posted notification id=" + 177); 4177 } 4178 Thread.sleep(500); // wait for notification listener to receive notification 4179 assertEquals(1, mListener.mPosted.size()); 4180 4181 NotificationListenerService.RankingMap rankingMap = mListener.mRankingMap; 4182 NotificationListenerService.Ranking outRanking = new NotificationListenerService.Ranking(); 4183 for (String key : rankingMap.getOrderedKeys()) { 4184 if (key.contains(mListener.getPackageName())) { 4185 rankingMap.getRanking(key, outRanking); 4186 assertTrue(outRanking.isConversation()); 4187 assertEquals(SHARE_SHORTCUT_ID, outRanking.getConversationShortcutInfo().getId()); 4188 } 4189 } 4190 } 4191 testDemoteConversationChannel()4192 public void testDemoteConversationChannel() { 4193 final NotificationChannel channel = 4194 new NotificationChannel(mId, "Messages", IMPORTANCE_DEFAULT); 4195 4196 String conversationId = "person a"; 4197 4198 final NotificationChannel conversationChannel = 4199 new NotificationChannel(mId + "child", 4200 "Messages from " + conversationId, IMPORTANCE_DEFAULT); 4201 conversationChannel.setConversationId(channel.getId(), conversationId); 4202 4203 mNotificationManager.createNotificationChannel(channel); 4204 mNotificationManager.createNotificationChannel(conversationChannel); 4205 4206 conversationChannel.setDemoted(true); 4207 4208 SystemUtil.runWithShellPermissionIdentity(() -> 4209 mNotificationManager.updateNotificationChannel( 4210 mContext.getPackageName(), android.os.Process.myUid(), channel)); 4211 4212 assertEquals(false, mNotificationManager.getNotificationChannel( 4213 channel.getId(), conversationId).isDemoted()); 4214 } 4215 testDeleteConversationChannels()4216 public void testDeleteConversationChannels() throws Exception { 4217 setUpNotifListener(); 4218 4219 createDynamicShortcut(); 4220 4221 final NotificationChannel channel = 4222 new NotificationChannel(mId, "Messages", IMPORTANCE_DEFAULT); 4223 4224 final NotificationChannel conversationChannel = 4225 new NotificationChannel(mId + "child", 4226 "Messages from " + SHARE_SHORTCUT_ID, IMPORTANCE_DEFAULT); 4227 conversationChannel.setConversationId(channel.getId(), SHARE_SHORTCUT_ID); 4228 4229 mNotificationManager.createNotificationChannel(channel); 4230 mNotificationManager.createNotificationChannel(conversationChannel); 4231 4232 mNotificationManager.notify(177, getConversationNotification().build()); 4233 4234 if (!checkNotificationExistence(177, /*shouldExist=*/ true)) { 4235 fail("couldn't find posted notification id=" + 177); 4236 } 4237 Thread.sleep(500); // wait for notification listener to receive notification 4238 assertEquals(1, mListener.mPosted.size()); 4239 4240 deleteShortcuts(); 4241 4242 Thread.sleep(300); // wait for deletion to propagate 4243 4244 assertFalse(mNotificationManager.getNotificationChannel(channel.getId(), 4245 conversationChannel.getConversationId()).isConversation()); 4246 4247 } 4248 4249 /** 4250 * This method verifies that an app can't bypass background restrictions by retrieving their own 4251 * notification and triggering it. 4252 */ 4253 @AsbSecurityTest(cveBugId = 185388103) testActivityStartFromRetrievedNotification_isBlocked()4254 public void testActivityStartFromRetrievedNotification_isBlocked() throws Exception { 4255 deactivateGracePeriod(); 4256 EventCallback callback = new EventCallback(); 4257 int notificationId = 6007; 4258 4259 // Post notification and fire its pending intent 4260 sendTrampolineMessage(TRAMPOLINE_SERVICE_API_30, MESSAGE_SERVICE_NOTIFICATION, 4261 notificationId, callback); 4262 PollingCheck.waitFor(TIMEOUT_MS, () -> uncheck(() -> { 4263 sendTrampolineMessage(TRAMPOLINE_SERVICE, MESSAGE_CLICK_NOTIFICATION, notificationId, 4264 callback); 4265 // timeoutMs = 1ms below because surrounding waitFor already handles retry & timeout. 4266 return callback.waitFor(EventCallback.NOTIFICATION_CLICKED, /* timeoutMs */ 1); 4267 })); 4268 4269 assertFalse("Activity start should have been blocked", 4270 callback.waitFor(EventCallback.ACTIVITY_STARTED, TIMEOUT_MS)); 4271 } 4272 testActivityStartOnBroadcastTrampoline_isBlocked()4273 public void testActivityStartOnBroadcastTrampoline_isBlocked() throws Exception { 4274 deactivateGracePeriod(); 4275 setUpNotifListener(); 4276 mListener.addTestPackage(TRAMPOLINE_APP); 4277 EventCallback callback = new EventCallback(); 4278 int notificationId = 6001; 4279 4280 // Post notification and fire its pending intent 4281 sendTrampolineMessage(TRAMPOLINE_SERVICE, MESSAGE_BROADCAST_NOTIFICATION, notificationId, 4282 callback); 4283 StatusBarNotification statusBarNotification = findPostedNotification(notificationId, true); 4284 assertNotNull("Notification not posted on time", statusBarNotification); 4285 statusBarNotification.getNotification().contentIntent.send(); 4286 4287 assertTrue("Broadcast not received on time", 4288 callback.waitFor(EventCallback.BROADCAST_RECEIVED, TIMEOUT_LONG_MS)); 4289 assertFalse("Activity start should have been blocked", 4290 callback.waitFor(EventCallback.ACTIVITY_STARTED, TIMEOUT_MS)); 4291 } 4292 testActivityStartOnServiceTrampoline_isBlocked()4293 public void testActivityStartOnServiceTrampoline_isBlocked() throws Exception { 4294 deactivateGracePeriod(); 4295 setUpNotifListener(); 4296 mListener.addTestPackage(TRAMPOLINE_APP); 4297 EventCallback callback = new EventCallback(); 4298 int notificationId = 6002; 4299 4300 // Post notification and fire its pending intent 4301 sendTrampolineMessage(TRAMPOLINE_SERVICE, MESSAGE_SERVICE_NOTIFICATION, notificationId, 4302 callback); 4303 StatusBarNotification statusBarNotification = findPostedNotification(notificationId, true); 4304 assertNotNull("Notification not posted on time", statusBarNotification); 4305 statusBarNotification.getNotification().contentIntent.send(); 4306 4307 assertTrue("Service not started on time", 4308 callback.waitFor(EventCallback.SERVICE_STARTED, TIMEOUT_MS)); 4309 assertFalse("Activity start should have been blocked", 4310 callback.waitFor(EventCallback.ACTIVITY_STARTED, TIMEOUT_MS)); 4311 } 4312 testActivityStartOnBroadcastTrampoline_whenApi30_isAllowed()4313 public void testActivityStartOnBroadcastTrampoline_whenApi30_isAllowed() throws Exception { 4314 deactivateGracePeriod(); 4315 setUpNotifListener(); 4316 mListener.addTestPackage(TRAMPOLINE_APP_API_30); 4317 EventCallback callback = new EventCallback(); 4318 int notificationId = 6003; 4319 4320 // Post notification and fire its pending intent 4321 sendTrampolineMessage(TRAMPOLINE_SERVICE_API_30, MESSAGE_BROADCAST_NOTIFICATION, 4322 notificationId, callback); 4323 StatusBarNotification statusBarNotification = findPostedNotification(notificationId, true); 4324 assertNotNull("Notification not posted on time", statusBarNotification); 4325 statusBarNotification.getNotification().contentIntent.send(); 4326 4327 assertTrue("Broadcast not received on time", 4328 callback.waitFor(EventCallback.BROADCAST_RECEIVED, TIMEOUT_LONG_MS)); 4329 assertTrue("Activity not started", 4330 callback.waitFor(EventCallback.ACTIVITY_STARTED, TIMEOUT_MS)); 4331 } 4332 testActivityStartOnServiceTrampoline_whenApi30_isAllowed()4333 public void testActivityStartOnServiceTrampoline_whenApi30_isAllowed() throws Exception { 4334 deactivateGracePeriod(); 4335 setUpNotifListener(); 4336 mListener.addTestPackage(TRAMPOLINE_APP_API_30); 4337 EventCallback callback = new EventCallback(); 4338 int notificationId = 6004; 4339 4340 // Post notification and fire its pending intent 4341 sendTrampolineMessage(TRAMPOLINE_SERVICE_API_30, MESSAGE_SERVICE_NOTIFICATION, 4342 notificationId, callback); 4343 StatusBarNotification statusBarNotification = findPostedNotification(notificationId, true); 4344 assertNotNull("Notification not posted on time", statusBarNotification); 4345 statusBarNotification.getNotification().contentIntent.send(); 4346 4347 assertTrue("Service not started on time", 4348 callback.waitFor(EventCallback.SERVICE_STARTED, TIMEOUT_MS)); 4349 assertTrue("Activity not started", 4350 callback.waitFor(EventCallback.ACTIVITY_STARTED, TIMEOUT_MS)); 4351 } 4352 testActivityStartOnBroadcastTrampoline_whenDefaultBrowser_isAllowed()4353 public void testActivityStartOnBroadcastTrampoline_whenDefaultBrowser_isAllowed() 4354 throws Exception { 4355 deactivateGracePeriod(); 4356 setDefaultBrowser(TRAMPOLINE_APP); 4357 setUpNotifListener(); 4358 mListener.addTestPackage(TRAMPOLINE_APP); 4359 EventCallback callback = new EventCallback(); 4360 int notificationId = 6005; 4361 4362 // Post notification and fire its pending intent 4363 sendTrampolineMessage(TRAMPOLINE_SERVICE, MESSAGE_BROADCAST_NOTIFICATION, notificationId, 4364 callback); 4365 StatusBarNotification statusBarNotification = findPostedNotification(notificationId, true); 4366 assertNotNull("Notification not posted on time", statusBarNotification); 4367 statusBarNotification.getNotification().contentIntent.send(); 4368 4369 assertTrue("Broadcast not received on time", 4370 callback.waitFor(EventCallback.BROADCAST_RECEIVED, TIMEOUT_LONG_MS)); 4371 assertTrue("Activity not started", 4372 callback.waitFor(EventCallback.ACTIVITY_STARTED, TIMEOUT_MS)); 4373 } 4374 testActivityStartOnServiceTrampoline_whenDefaultBrowser_isAllowed()4375 public void testActivityStartOnServiceTrampoline_whenDefaultBrowser_isAllowed() 4376 throws Exception { 4377 deactivateGracePeriod(); 4378 setDefaultBrowser(TRAMPOLINE_APP); 4379 setUpNotifListener(); 4380 mListener.addTestPackage(TRAMPOLINE_APP); 4381 EventCallback callback = new EventCallback(); 4382 int notificationId = 6006; 4383 4384 // Post notification and fire its pending intent 4385 sendTrampolineMessage(TRAMPOLINE_SERVICE, MESSAGE_SERVICE_NOTIFICATION, notificationId, 4386 callback); 4387 StatusBarNotification statusBarNotification = findPostedNotification(notificationId, true); 4388 assertNotNull("Notification not posted on time", statusBarNotification); 4389 statusBarNotification.getNotification().contentIntent.send(); 4390 4391 assertTrue("Service not started on time", 4392 callback.waitFor(EventCallback.SERVICE_STARTED, TIMEOUT_MS)); 4393 assertTrue("Activity not started", 4394 callback.waitFor(EventCallback.ACTIVITY_STARTED, TIMEOUT_MS)); 4395 } 4396 testGrantRevokeNotificationManagerApis_works()4397 public void testGrantRevokeNotificationManagerApis_works() { 4398 SystemUtil.runWithShellPermissionIdentity(() -> { 4399 ComponentName componentName = TestNotificationListener.getComponentName(); 4400 mNotificationManager.setNotificationListenerAccessGranted( 4401 componentName, true, true); 4402 4403 assertThat( 4404 mNotificationManager.getEnabledNotificationListeners(), 4405 hasItem(componentName)); 4406 4407 mNotificationManager.setNotificationListenerAccessGranted( 4408 componentName, false, false); 4409 4410 assertThat( 4411 "Non-user-set changes should not override user-set", 4412 mNotificationManager.getEnabledNotificationListeners(), 4413 hasItem(componentName)); 4414 }); 4415 } 4416 testGrantRevokeNotificationManagerApis_exclusiveToPermissionController()4417 public void testGrantRevokeNotificationManagerApis_exclusiveToPermissionController() { 4418 List<PackageInfo> allPackages = mPackageManager.getInstalledPackages( 4419 PackageManager.MATCH_DISABLED_COMPONENTS 4420 | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS); 4421 List<String> allowedPackages = Arrays.asList( 4422 mPackageManager.getPermissionControllerPackageName(), 4423 "com.android.shell"); 4424 for (PackageInfo pkg : allPackages) { 4425 if (!pkg.applicationInfo.isSystemApp() 4426 && mPackageManager.checkPermission( 4427 Manifest.permission.MANAGE_NOTIFICATION_LISTENERS, pkg.packageName) 4428 == PackageManager.PERMISSION_GRANTED 4429 && !allowedPackages.contains(pkg.packageName)) { 4430 fail(pkg.packageName + " can't hold " 4431 + Manifest.permission.MANAGE_NOTIFICATION_LISTENERS); 4432 } 4433 } 4434 } 4435 testChannelDeletion_cancelReason()4436 public void testChannelDeletion_cancelReason() throws Exception { 4437 setUpNotifListener(); 4438 4439 sendNotification(566, R.drawable.black); 4440 4441 Thread.sleep(500); // wait for notification listener to receive notification 4442 assertEquals(1, mListener.mPosted.size()); 4443 String key = mListener.mPosted.get(0).getKey(); 4444 4445 mNotificationManager.deleteNotificationChannel(NOTIFICATION_CHANNEL_ID); 4446 4447 assertEquals(NotificationListenerService.REASON_CHANNEL_REMOVED, 4448 getCancellationReason(key)); 4449 } 4450 4451 private static class EventCallback extends Handler { 4452 private static final int BROADCAST_RECEIVED = 1; 4453 private static final int SERVICE_STARTED = 2; 4454 private static final int ACTIVITY_STARTED = 3; 4455 private static final int NOTIFICATION_CLICKED = 4; 4456 4457 private final Map<Integer, CompletableFuture<Integer>> mEvents = 4458 Collections.synchronizedMap(new ArrayMap<>()); 4459 EventCallback()4460 private EventCallback() { 4461 super(Looper.getMainLooper()); 4462 } 4463 4464 @Override handleMessage(Message message)4465 public void handleMessage(Message message) { 4466 mEvents.computeIfAbsent(message.what, e -> new CompletableFuture<>()).complete( 4467 message.arg1); 4468 } 4469 waitFor(int event, long timeoutMs)4470 public boolean waitFor(int event, long timeoutMs) { 4471 try { 4472 return mEvents.computeIfAbsent(event, e -> new CompletableFuture<>()).get(timeoutMs, 4473 TimeUnit.MILLISECONDS) == 0; 4474 } catch (InterruptedException | ExecutionException | TimeoutException e) { 4475 return false; 4476 } 4477 } 4478 } 4479 } 4480