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