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