1 /*
2  * Copyright (C) 2017 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 com.android.systemui;
18 
19 import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
20 
21 import static junit.framework.Assert.assertEquals;
22 import static junit.framework.Assert.assertNull;
23 import static junit.framework.TestCase.fail;
24 
25 import static org.junit.Assert.assertFalse;
26 import static org.junit.Assert.assertTrue;
27 import static org.mockito.ArgumentMatchers.anyString;
28 import static org.mockito.Mockito.mock;
29 import static org.mockito.Mockito.never;
30 import static org.mockito.Mockito.times;
31 import static org.mockito.Mockito.verify;
32 import static org.mockito.Mockito.when;
33 
34 import android.annotation.UserIdInt;
35 import android.app.AppOpsManager;
36 import android.app.Notification;
37 import android.app.NotificationManager;
38 import android.os.Bundle;
39 import android.os.Handler;
40 import android.os.UserHandle;
41 import android.service.notification.StatusBarNotification;
42 import android.testing.AndroidTestingRunner;
43 import android.testing.TestableLooper;
44 import android.widget.RemoteViews;
45 
46 import androidx.test.filters.SmallTest;
47 
48 import com.android.internal.messages.nano.SystemMessageProto;
49 import com.android.systemui.appops.AppOpsController;
50 import com.android.systemui.statusbar.notification.NotificationEntryListener;
51 import com.android.systemui.statusbar.notification.NotificationEntryManager;
52 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
53 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
54 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
55 import com.android.systemui.util.time.FakeSystemClock;
56 
57 import junit.framework.Assert;
58 
59 import org.junit.Before;
60 import org.junit.Test;
61 import org.junit.runner.RunWith;
62 import org.mockito.ArgumentCaptor;
63 import org.mockito.Mock;
64 import org.mockito.MockitoAnnotations;
65 
66 @SmallTest
67 @RunWith(AndroidTestingRunner.class)
68 @TestableLooper.RunWithLooper
69 public class ForegroundServiceControllerTest extends SysuiTestCase {
70     private ForegroundServiceController mFsc;
71     private ForegroundServiceNotificationListener mListener;
72     private NotificationEntryListener mEntryListener;
73     private final FakeSystemClock mClock = new FakeSystemClock();
74     @Mock private NotificationEntryManager mEntryManager;
75     @Mock private AppOpsController mAppOpsController;
76     @Mock private Handler mMainHandler;
77     @Mock private NotifPipeline mNotifPipeline;
78 
79     @Before
setUp()80     public void setUp() throws Exception {
81         // allow the TestLooper to be asserted as the main thread these tests
82         allowTestableLooperAsMainThread();
83 
84         MockitoAnnotations.initMocks(this);
85         mFsc = new ForegroundServiceController(
86                 mEntryManager, mAppOpsController, mMainHandler);
87         mListener = new ForegroundServiceNotificationListener(
88                 mContext, mFsc, mEntryManager, mNotifPipeline,
89                 mock(ForegroundServiceLifetimeExtender.class), mClock);
90         ArgumentCaptor<NotificationEntryListener> entryListenerCaptor =
91                 ArgumentCaptor.forClass(NotificationEntryListener.class);
92         verify(mEntryManager).addNotificationEntryListener(
93                 entryListenerCaptor.capture());
94         mEntryListener = entryListenerCaptor.getValue();
95     }
96 
97     @Test
testAppOpsChangedCalledFromBgThread()98     public void testAppOpsChangedCalledFromBgThread() {
99         try {
100             // WHEN onAppOpChanged is called from a different thread than the MainLooper
101             disallowTestableLooperAsMainThread();
102             NotificationEntry entry = createFgEntry();
103             mFsc.onAppOpChanged(
104                     AppOpsManager.OP_CAMERA,
105                     entry.getSbn().getUid(),
106                     entry.getSbn().getPackageName(),
107                     true);
108 
109             // This test is run on the TestableLooper, which is not the MainLooper, so
110             // we expect an exception to be thrown
111             fail("onAppOpChanged shouldn't be allowed to be called from a bg thread.");
112         } catch (IllegalStateException e) {
113             // THEN expect an exception
114         }
115     }
116 
117     @Test
testAppOps_appOpChangedBeforeNotificationExists()118     public void testAppOps_appOpChangedBeforeNotificationExists() {
119         // GIVEN app op exists, but notification doesn't exist in NEM yet
120         NotificationEntry entry = createFgEntry();
121         mFsc.onAppOpChanged(
122                 AppOpsManager.OP_CAMERA,
123                 entry.getSbn().getUid(),
124                 entry.getSbn().getPackageName(),
125                 true);
126         assertFalse(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA));
127 
128         // WHEN the notification is added
129         mEntryListener.onPendingEntryAdded(entry);
130 
131         // THEN the app op is added to the entry
132         Assert.assertTrue(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA));
133     }
134 
135     @Test
testAppOps_appOpAddedToForegroundNotif()136     public void testAppOps_appOpAddedToForegroundNotif() {
137         // GIVEN a notification associated with a foreground service
138         NotificationEntry entry = addFgEntry();
139         when(mEntryManager.getPendingOrActiveNotif(entry.getKey())).thenReturn(entry);
140 
141         // WHEN we are notified of a new app op for this notification
142         mFsc.onAppOpChanged(
143                 AppOpsManager.OP_CAMERA,
144                 entry.getSbn().getUid(),
145                 entry.getSbn().getPackageName(),
146                 true);
147 
148         // THEN the app op is added to the entry
149         Assert.assertTrue(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA));
150 
151         // THEN notification views are updated since the notification is visible
152         verify(mEntryManager, times(1)).updateNotifications(anyString());
153     }
154 
155     @Test
testAppOpsAlreadyAdded()156     public void testAppOpsAlreadyAdded() {
157         // GIVEN a foreground service associated notification that already has the correct app op
158         NotificationEntry entry = addFgEntry();
159         entry.mActiveAppOps.add(AppOpsManager.OP_CAMERA);
160         when(mEntryManager.getPendingOrActiveNotif(entry.getKey())).thenReturn(entry);
161 
162         // WHEN we are notified of the same app op for this notification
163         mFsc.onAppOpChanged(
164                 AppOpsManager.OP_CAMERA,
165                 entry.getSbn().getUid(),
166                 entry.getSbn().getPackageName(),
167                 true);
168 
169         // THEN the app op still exists in the notification entry
170         Assert.assertTrue(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA));
171 
172         // THEN notification views aren't updated since nothing changed
173         verify(mEntryManager, never()).updateNotifications(anyString());
174     }
175 
176     @Test
testAppOps_appOpNotAddedToUnrelatedNotif()177     public void testAppOps_appOpNotAddedToUnrelatedNotif() {
178         // GIVEN no notification entries correspond to the newly updated appOp
179         NotificationEntry entry = addFgEntry();
180         when(mEntryManager.getPendingOrActiveNotif(entry.getKey())).thenReturn(null);
181 
182         // WHEN a new app op is detected
183         mFsc.onAppOpChanged(
184                 AppOpsManager.OP_CAMERA,
185                 entry.getSbn().getUid(),
186                 entry.getSbn().getPackageName(),
187                 true);
188 
189         // THEN we won't see appOps on the entry
190         Assert.assertFalse(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA));
191 
192         // THEN notification views aren't updated since nothing changed
193         verify(mEntryManager, never()).updateNotifications(anyString());
194     }
195 
196     @Test
testAppOpsCRUD()197     public void testAppOpsCRUD() {
198         // no crash on remove that doesn't exist
199         mFsc.onAppOpChanged(9, 1000, "pkg1", false);
200         assertNull(mFsc.getAppOps(0, "pkg1"));
201 
202         // multiuser & multipackage
203         mFsc.onAppOpChanged(8, 50, "pkg1", true);
204         mFsc.onAppOpChanged(1, 60, "pkg3", true);
205         mFsc.onAppOpChanged(7, 500000, "pkg2", true);
206 
207         assertEquals(1, mFsc.getAppOps(0, "pkg1").size());
208         assertTrue(mFsc.getAppOps(0, "pkg1").contains(8));
209 
210         assertEquals(1, mFsc.getAppOps(UserHandle.getUserId(500000), "pkg2").size());
211         assertTrue(mFsc.getAppOps(UserHandle.getUserId(500000), "pkg2").contains(7));
212 
213         assertEquals(1, mFsc.getAppOps(0, "pkg3").size());
214         assertTrue(mFsc.getAppOps(0, "pkg3").contains(1));
215 
216         // multiple ops for the same package
217         mFsc.onAppOpChanged(9, 50, "pkg1", true);
218         mFsc.onAppOpChanged(5, 50, "pkg1", true);
219 
220         assertEquals(3, mFsc.getAppOps(0, "pkg1").size());
221         assertTrue(mFsc.getAppOps(0, "pkg1").contains(8));
222         assertTrue(mFsc.getAppOps(0, "pkg1").contains(9));
223         assertTrue(mFsc.getAppOps(0, "pkg1").contains(5));
224 
225         assertEquals(1, mFsc.getAppOps(UserHandle.getUserId(500000), "pkg2").size());
226         assertTrue(mFsc.getAppOps(UserHandle.getUserId(500000), "pkg2").contains(7));
227 
228         // remove one of the multiples
229         mFsc.onAppOpChanged(9, 50, "pkg1", false);
230         assertEquals(2, mFsc.getAppOps(0, "pkg1").size());
231         assertTrue(mFsc.getAppOps(0, "pkg1").contains(8));
232         assertTrue(mFsc.getAppOps(0, "pkg1").contains(5));
233 
234         // remove last op
235         mFsc.onAppOpChanged(1, 60, "pkg3", false);
236         assertNull(mFsc.getAppOps(0, "pkg3"));
237     }
238 
239     @Test
testDisclosurePredicate()240     public void testDisclosurePredicate() {
241         StatusBarNotification sbn_user1_app1 = makeMockSBN(USERID_ONE, "com.example.app1",
242                 5000, "monkeys", Notification.FLAG_AUTO_CANCEL);
243         StatusBarNotification sbn_user1_disclosure = makeMockSBN(USERID_ONE, "android",
244                 SystemMessageProto.SystemMessage.NOTE_FOREGROUND_SERVICES,
245                 null, Notification.FLAG_NO_CLEAR);
246 
247         assertTrue(mFsc.isDisclosureNotification(sbn_user1_disclosure));
248         assertFalse(mFsc.isDisclosureNotification(sbn_user1_app1));
249     }
250 
251     @Test
testNeedsDisclosureAfterRemovingUnrelatedNotification()252     public void testNeedsDisclosureAfterRemovingUnrelatedNotification() {
253         final String PKG1 = "com.example.app100";
254 
255         StatusBarNotification sbn_user1_app1 = makeMockSBN(USERID_ONE, PKG1,
256                 5000, "monkeys", Notification.FLAG_AUTO_CANCEL);
257         StatusBarNotification sbn_user1_app1_fg = makeMockFgSBN(USERID_ONE, PKG1);
258 
259         // first add a normal notification
260         entryAdded(sbn_user1_app1, NotificationManager.IMPORTANCE_DEFAULT);
261         // nothing required yet
262         assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE));
263         // now the app starts a fg service
264         entryAdded(makeMockDisclosure(USERID_ONE, new String[]{PKG1}),
265                 NotificationManager.IMPORTANCE_DEFAULT);
266         assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE)); // should be required!
267         // add the fg notification
268         entryAdded(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_DEFAULT);
269         assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE)); // app1 has got it covered
270         // remove the boring notification
271         entryRemoved(sbn_user1_app1);
272         assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE)); // app1 has STILL got it covered
273         entryRemoved(sbn_user1_app1_fg);
274         assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE)); // should be required!
275     }
276 
277     @Test
testSimpleAddRemove()278     public void testSimpleAddRemove() {
279         final String PKG1 = "com.example.app1";
280         final String PKG2 = "com.example.app2";
281 
282         StatusBarNotification sbn_user1_app1 = makeMockSBN(USERID_ONE, PKG1,
283                 5000, "monkeys", Notification.FLAG_AUTO_CANCEL);
284         entryAdded(sbn_user1_app1, NotificationManager.IMPORTANCE_DEFAULT);
285 
286         // no services are "running"
287         entryAdded(makeMockDisclosure(USERID_ONE, null),
288                 NotificationManager.IMPORTANCE_DEFAULT);
289 
290         assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE));
291         assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
292 
293         entryUpdated(makeMockDisclosure(USERID_ONE, new String[]{PKG1}),
294                 NotificationManager.IMPORTANCE_DEFAULT);
295         assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE)); // should be required!
296         assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
297 
298         // switch to different package
299         entryUpdated(makeMockDisclosure(USERID_ONE, new String[]{PKG2}),
300                 NotificationManager.IMPORTANCE_DEFAULT);
301         assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE));
302         assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
303 
304         entryUpdated(makeMockDisclosure(USERID_TWO, new String[]{PKG1}),
305                 NotificationManager.IMPORTANCE_DEFAULT);
306         assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE));
307         assertTrue(mFsc.isDisclosureNeededForUser(USERID_TWO)); // finally user2 needs one too
308 
309         entryUpdated(makeMockDisclosure(USERID_ONE, new String[]{PKG2, PKG1}),
310                 NotificationManager.IMPORTANCE_DEFAULT);
311         assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE));
312         assertTrue(mFsc.isDisclosureNeededForUser(USERID_TWO));
313 
314         entryRemoved(makeMockDisclosure(USERID_ONE, null /*unused*/));
315         assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE));
316         assertTrue(mFsc.isDisclosureNeededForUser(USERID_TWO));
317 
318         entryRemoved(makeMockDisclosure(USERID_TWO, null /*unused*/));
319         assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE));
320         assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
321     }
322 
323     @Test
testDisclosureBasic()324     public void testDisclosureBasic() {
325         final String PKG1 = "com.example.app0";
326 
327         StatusBarNotification sbn_user1_app1 = makeMockSBN(USERID_ONE, PKG1,
328                 5000, "monkeys", Notification.FLAG_AUTO_CANCEL);
329         StatusBarNotification sbn_user1_app1_fg = makeMockFgSBN(USERID_ONE, PKG1);
330 
331         entryAdded(sbn_user1_app1, NotificationManager.IMPORTANCE_DEFAULT); // not fg
332         entryAdded(makeMockDisclosure(USERID_ONE, new String[]{PKG1}),
333                 NotificationManager.IMPORTANCE_DEFAULT);
334         assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE)); // should be required!
335         entryAdded(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_DEFAULT);
336         assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE)); // app1 has got it covered
337         assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
338 
339         // let's take out the other notification and see what happens.
340 
341         entryRemoved(sbn_user1_app1);
342         assertFalse(
343                 mFsc.isDisclosureNeededForUser(USERID_ONE)); // still covered by sbn_user1_app1_fg
344         assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
345 
346         // let's attempt to downgrade the notification from FLAG_FOREGROUND and see what we get
347         StatusBarNotification sbn_user1_app1_fg_sneaky = makeMockFgSBN(USERID_ONE, PKG1);
348         sbn_user1_app1_fg_sneaky.getNotification().flags = 0;
349         entryUpdated(sbn_user1_app1_fg_sneaky,
350                 NotificationManager.IMPORTANCE_DEFAULT);
351         assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE)); // should be required!
352         assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
353 
354         // ok, ok, we'll put it back
355         sbn_user1_app1_fg_sneaky.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
356         entryUpdated(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_DEFAULT);
357         assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE));
358         assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
359 
360         entryRemoved(sbn_user1_app1_fg_sneaky);
361         assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE)); // should be required!
362         assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
363 
364         // now let's test an upgrade
365         entryAdded(sbn_user1_app1, NotificationManager.IMPORTANCE_DEFAULT);
366         assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE));
367         assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
368         sbn_user1_app1.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
369         entryUpdated(sbn_user1_app1,
370                 NotificationManager.IMPORTANCE_DEFAULT); // this is now a fg notification
371 
372         assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
373         assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE));
374 
375         // remove it, make sure we're out of compliance again
376         entryRemoved(sbn_user1_app1); // was fg, should return true
377         entryRemoved(sbn_user1_app1);
378         assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
379         assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE));
380 
381         // importance upgrade
382         entryAdded(sbn_user1_app1_fg, NotificationManager.IMPORTANCE_MIN);
383         assertTrue(mFsc.isDisclosureNeededForUser(USERID_ONE));
384         assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
385         sbn_user1_app1.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
386         entryUpdated(sbn_user1_app1_fg,
387                 NotificationManager.IMPORTANCE_DEFAULT); // this is now a fg notification
388 
389         // finally, let's turn off the service
390         entryAdded(makeMockDisclosure(USERID_ONE, null),
391                 NotificationManager.IMPORTANCE_DEFAULT);
392 
393         assertFalse(mFsc.isDisclosureNeededForUser(USERID_ONE));
394         assertFalse(mFsc.isDisclosureNeededForUser(USERID_TWO));
395     }
396 
397     @Test
testOverlayPredicate()398     public void testOverlayPredicate() {
399         StatusBarNotification sbn_user1_app1 = makeMockSBN(USERID_ONE, "com.example.app1",
400                 5000, "monkeys", Notification.FLAG_AUTO_CANCEL);
401         StatusBarNotification sbn_user1_overlay = makeMockSBN(USERID_ONE, "android",
402                 0, "AlertWindowNotification", Notification.FLAG_NO_CLEAR);
403 
404         assertTrue(mFsc.isSystemAlertNotification(sbn_user1_overlay));
405         assertFalse(mFsc.isSystemAlertNotification(sbn_user1_app1));
406     }
407 
408     @Test
testNoNotifsNorAppOps_noSystemAlertWarningRequired()409     public void testNoNotifsNorAppOps_noSystemAlertWarningRequired() {
410         // no notifications nor app op signals that this package/userId requires system alert
411         // warning
412         assertFalse(mFsc.isSystemAlertWarningNeeded(USERID_ONE, "any"));
413     }
414 
415     @Test
testCustomLayouts_systemAlertWarningRequired()416     public void testCustomLayouts_systemAlertWarningRequired() {
417         // GIVEN a notification with a custom layout
418         final String pkg = "com.example.app0";
419         StatusBarNotification customLayoutNotif = makeMockSBN(USERID_ONE, pkg, 0,
420                 false);
421 
422         // WHEN the custom layout entry is added
423         entryAdded(customLayoutNotif, NotificationManager.IMPORTANCE_MIN);
424 
425         // THEN a system alert warning is required since there aren't any notifications that can
426         // display the app ops
427         assertTrue(mFsc.isSystemAlertWarningNeeded(USERID_ONE, pkg));
428     }
429 
430     @Test
testStandardLayoutExists_noSystemAlertWarningRequired()431     public void testStandardLayoutExists_noSystemAlertWarningRequired() {
432         // GIVEN two notifications (one with a custom layout, the other with a standard layout)
433         final String pkg = "com.example.app0";
434         StatusBarNotification customLayoutNotif = makeMockSBN(USERID_ONE, pkg, 0,
435                 false);
436         StatusBarNotification standardLayoutNotif = makeMockSBN(USERID_ONE, pkg, 1, true);
437 
438         // WHEN the entries are added
439         entryAdded(customLayoutNotif, NotificationManager.IMPORTANCE_MIN);
440         entryAdded(standardLayoutNotif, NotificationManager.IMPORTANCE_MIN);
441 
442         // THEN no system alert warning is required, since there is at least one notification
443         // with a standard layout that can display the app ops on the notification
444         assertFalse(mFsc.isSystemAlertWarningNeeded(USERID_ONE, pkg));
445     }
446 
447     @Test
testStandardLayoutRemoved_systemAlertWarningRequired()448     public void testStandardLayoutRemoved_systemAlertWarningRequired() {
449         // GIVEN two notifications (one with a custom layout, the other with a standard layout)
450         final String pkg = "com.example.app0";
451         StatusBarNotification customLayoutNotif = makeMockSBN(USERID_ONE, pkg, 0,
452                 false);
453         StatusBarNotification standardLayoutNotif = makeMockSBN(USERID_ONE, pkg, 1, true);
454 
455         // WHEN the entries are added and then the standard layout notification is removed
456         entryAdded(customLayoutNotif, NotificationManager.IMPORTANCE_MIN);
457         entryAdded(standardLayoutNotif, NotificationManager.IMPORTANCE_MIN);
458         entryRemoved(standardLayoutNotif);
459 
460         // THEN a system alert warning is required since there aren't any notifications that can
461         // display the app ops
462         assertTrue(mFsc.isSystemAlertWarningNeeded(USERID_ONE, pkg));
463     }
464 
465     @Test
testStandardLayoutUpdatedToCustomLayout_systemAlertWarningRequired()466     public void testStandardLayoutUpdatedToCustomLayout_systemAlertWarningRequired() {
467         // GIVEN a standard layout notification and then an updated version with a customLayout
468         final String pkg = "com.example.app0";
469         StatusBarNotification standardLayoutNotif = makeMockSBN(USERID_ONE, pkg, 1, true);
470         StatusBarNotification updatedToCustomLayoutNotif = makeMockSBN(USERID_ONE, pkg, 1, false);
471 
472         // WHEN the entries is added and then updated to a custom layout
473         entryAdded(standardLayoutNotif, NotificationManager.IMPORTANCE_MIN);
474         entryUpdated(updatedToCustomLayoutNotif, NotificationManager.IMPORTANCE_MIN);
475 
476         // THEN a system alert warning is required since there aren't any notifications that can
477         // display the app ops
478         assertTrue(mFsc.isSystemAlertWarningNeeded(USERID_ONE, pkg));
479     }
480 
makeMockSBN(int userId, String pkg, int id, String tag, int flags)481     private StatusBarNotification makeMockSBN(int userId, String pkg, int id, String tag,
482             int flags) {
483         final Notification n = mock(Notification.class);
484         n.extras = new Bundle();
485         n.flags = flags;
486         return makeMockSBN(userId, pkg, id, tag, n);
487     }
488 
makeMockSBN(int userid, String pkg, int id, String tag, Notification n)489     private StatusBarNotification makeMockSBN(int userid, String pkg, int id, String tag,
490             Notification n) {
491         final StatusBarNotification sbn = mock(StatusBarNotification.class);
492         when(sbn.getNotification()).thenReturn(n);
493         when(sbn.getId()).thenReturn(id);
494         when(sbn.getPackageName()).thenReturn(pkg);
495         when(sbn.getTag()).thenReturn(tag);
496         when(sbn.getUserId()).thenReturn(userid);
497         when(sbn.getUser()).thenReturn(new UserHandle(userid));
498         when(sbn.getKey()).thenReturn("MOCK:"+userid+"|"+pkg+"|"+id+"|"+tag);
499         return sbn;
500     }
501 
makeMockSBN(int uid, String pkg, int id, boolean usesStdLayout)502     private StatusBarNotification makeMockSBN(int uid, String pkg, int id,
503             boolean usesStdLayout) {
504         StatusBarNotification sbn = makeMockSBN(uid, pkg, id, "foo", 0);
505         if (usesStdLayout) {
506             sbn.getNotification().contentView = null;
507             sbn.getNotification().headsUpContentView = null;
508             sbn.getNotification().bigContentView = null;
509         } else {
510             sbn.getNotification().contentView = mock(RemoteViews.class);
511         }
512         return sbn;
513     }
514 
makeMockFgSBN(int uid, String pkg, int id, boolean usesStdLayout)515     private StatusBarNotification makeMockFgSBN(int uid, String pkg, int id,
516             boolean usesStdLayout) {
517         StatusBarNotification sbn =
518                 makeMockSBN(uid, pkg, id, "foo", Notification.FLAG_FOREGROUND_SERVICE);
519         if (usesStdLayout) {
520             sbn.getNotification().contentView = null;
521             sbn.getNotification().headsUpContentView = null;
522             sbn.getNotification().bigContentView = null;
523         } else {
524             sbn.getNotification().contentView = mock(RemoteViews.class);
525         }
526         return sbn;
527     }
528 
makeMockFgSBN(int uid, String pkg)529     private StatusBarNotification makeMockFgSBN(int uid, String pkg) {
530         return makeMockSBN(uid, pkg, 1000, "foo", Notification.FLAG_FOREGROUND_SERVICE);
531     }
532 
makeMockDisclosure(int userid, String[] pkgs)533     private StatusBarNotification makeMockDisclosure(int userid, String[] pkgs) {
534         final Notification n = mock(Notification.class);
535         n.flags = Notification.FLAG_ONGOING_EVENT;
536         final Bundle extras = new Bundle();
537         if (pkgs != null) extras.putStringArray(Notification.EXTRA_FOREGROUND_APPS, pkgs);
538         n.extras = extras;
539         n.when = System.currentTimeMillis() - 10000; // ten seconds ago
540         final StatusBarNotification sbn = makeMockSBN(userid, "android",
541                 SystemMessageProto.SystemMessage.NOTE_FOREGROUND_SERVICES,
542                 null, n);
543         sbn.getNotification().extras = extras;
544         return sbn;
545     }
546 
addFgEntry()547     private NotificationEntry addFgEntry() {
548         NotificationEntry entry = createFgEntry();
549         mEntryListener.onPendingEntryAdded(entry);
550         return entry;
551     }
552 
createFgEntry()553     private NotificationEntry createFgEntry() {
554         return new NotificationEntryBuilder()
555                 .setSbn(makeMockFgSBN(0, TEST_PACKAGE_NAME, 1000, true))
556                 .setImportance(NotificationManager.IMPORTANCE_DEFAULT)
557                 .build();
558     }
559 
entryRemoved(StatusBarNotification notification)560     private void entryRemoved(StatusBarNotification notification) {
561         mEntryListener.onEntryRemoved(
562                 new NotificationEntryBuilder()
563                         .setSbn(notification)
564                         .build(),
565                 null,
566                 false,
567                 REASON_APP_CANCEL);
568     }
569 
entryAdded(StatusBarNotification notification, int importance)570     private void entryAdded(StatusBarNotification notification, int importance) {
571         NotificationEntry entry = new NotificationEntryBuilder()
572                 .setSbn(notification)
573                 .setImportance(importance)
574                 .build();
575         mEntryListener.onPendingEntryAdded(entry);
576     }
577 
entryUpdated(StatusBarNotification notification, int importance)578     private void entryUpdated(StatusBarNotification notification, int importance) {
579         NotificationEntry entry = new NotificationEntryBuilder()
580                 .setSbn(notification)
581                 .setImportance(importance)
582                 .build();
583         mEntryListener.onPreEntryUpdated(entry);
584     }
585 
586     @UserIdInt private static final int USERID_ONE = 10; // UserManagerService.MIN_USER_ID;
587     @UserIdInt private static final int USERID_TWO = USERID_ONE + 1;
588     private static final String TEST_PACKAGE_NAME = "test";
589 }
590