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