1 /*
2  * Copyright (C) 2023 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.server;
18 
19 import static android.permission.flags.Flags.FLAG_SENSITIVE_CONTENT_METRICS_BUGFIX;
20 import static android.permission.flags.Flags.FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION;
21 
22 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
23 
24 import static org.mockito.ArgumentMatchers.any;
25 import static org.mockito.ArgumentMatchers.anyLong;
26 import static org.mockito.ArgumentMatchers.eq;
27 import static org.mockito.Mockito.doCallRealMethod;
28 import static org.mockito.Mockito.doReturn;
29 import static org.mockito.Mockito.doThrow;
30 import static org.mockito.Mockito.never;
31 import static org.mockito.Mockito.spy;
32 import static org.mockito.Mockito.verify;
33 import static org.mockito.Mockito.when;
34 
35 import android.content.pm.PackageManagerInternal;
36 import android.media.projection.MediaProjectionInfo;
37 import android.media.projection.MediaProjectionManager;
38 import android.os.Process;
39 import android.platform.test.annotations.RequiresFlagsDisabled;
40 import android.platform.test.annotations.RequiresFlagsEnabled;
41 import android.platform.test.flag.junit.CheckFlagsRule;
42 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
43 import android.provider.Settings;
44 import android.service.notification.NotificationListenerService.Ranking;
45 import android.service.notification.NotificationListenerService.RankingMap;
46 import android.service.notification.StatusBarNotification;
47 import android.testing.AndroidTestingRunner;
48 import android.testing.TestableContext;
49 import android.testing.TestableLooper.RunWithLooper;
50 import android.util.ArraySet;
51 
52 import androidx.test.filters.SmallTest;
53 
54 import com.android.server.wm.SensitiveContentPackages.PackageInfo;
55 import com.android.server.wm.WindowManagerInternal;
56 
57 import org.junit.After;
58 import org.junit.Before;
59 import org.junit.Rule;
60 import org.junit.Test;
61 import org.junit.runner.RunWith;
62 import org.mockito.ArgumentCaptor;
63 import org.mockito.Captor;
64 import org.mockito.Mock;
65 import org.mockito.Mockito;
66 import org.mockito.MockitoAnnotations;
67 
68 import java.util.Set;
69 
70 @SmallTest
71 @RunWith(AndroidTestingRunner.class)
72 @RunWithLooper
73 @RequiresFlagsEnabled(FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION)
74 /**
75  * Test {@link SensitiveContentProtectionManagerService} for sensitive notification protection,
76  * the service protects sensitive content during screen share.
77  */
78 public class SensitiveContentProtectionManagerServiceNotificationTest {
79     private static final String NOTIFICATION_KEY_1 = "com.android.server.notification.TEST_KEY_1";
80     private static final String NOTIFICATION_KEY_2 = "com.android.server.notification.TEST_KEY_2";
81 
82     private static final String NOTIFICATION_PKG_1 = "com.android.server.notification.one";
83     private static final String NOTIFICATION_PKG_2 = "com.android.server.notification.two";
84 
85     private static final String SCREEN_RECORDER_PACKAGE = "test.screen.recorder.package";
86     private static final String EXEMPTED_SCREEN_RECORDER_PACKAGE = "exempt.screen.recorder.package";
87 
88     private static final int NOTIFICATION_UID_1 = 5;
89     private static final int NOTIFICATION_UID_2 = 6;
90 
91     private static final ArraySet<PackageInfo> EMPTY_SET = new ArraySet<>();
92 
93     @Rule
94     public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
95 
96     @Rule
97     public final TestableContext mContext =
98             new TestableContext(getInstrumentation().getTargetContext(), null);
99 
100     private SensitiveContentProtectionManagerService mSensitiveContentProtectionManagerService;
101 
102     @Captor
103     ArgumentCaptor<MediaProjectionManager.Callback> mMediaProjectionCallbackCaptor;
104     @Captor
105     private ArgumentCaptor<ArraySet<PackageInfo>> mPackageInfoCaptor;
106 
107     @Mock
108     private MediaProjectionManager mProjectionManager;
109 
110     @Mock
111     private WindowManagerInternal mWindowManager;
112 
113     @Mock
114     private PackageManagerInternal mPackageManagerInternal;
115 
116     @Mock
117     private StatusBarNotification mNotification1;
118 
119     @Mock
120     private StatusBarNotification mNotification2;
121 
122     @Mock
123     private RankingMap mRankingMap;
124 
125     @Mock
126     private Ranking mSensitiveRanking;
127 
128     @Mock
129     private Ranking mNonSensitiveRanking;
130 
131     @Before
setUp()132     public void setUp() {
133         MockitoAnnotations.initMocks(this);
134 
135         mSensitiveContentProtectionManagerService =
136                 new SensitiveContentProtectionManagerService(mContext);
137 
138         mSensitiveContentProtectionManagerService.mNotificationListener =
139                 spy(mSensitiveContentProtectionManagerService.mNotificationListener);
140         doCallRealMethod()
141                 .when(mSensitiveContentProtectionManagerService.mNotificationListener)
142                 .onListenerConnected();
143 
144         // Setup RankingMap and two possilbe rankings
145         when(mSensitiveRanking.hasSensitiveContent()).thenReturn(true);
146         when(mNonSensitiveRanking.hasSensitiveContent()).thenReturn(false);
147         doReturn(mRankingMap)
148                 .when(mSensitiveContentProtectionManagerService.mNotificationListener)
149                 .getCurrentRanking();
150 
151         setupSensitiveNotification();
152 
153         mSensitiveContentProtectionManagerService.init(mProjectionManager, mWindowManager,
154                 mPackageManagerInternal, new ArraySet<>(Set.of(EXEMPTED_SCREEN_RECORDER_PACKAGE)));
155 
156         // Obtain useful mMediaProjectionCallback
157         verify(mProjectionManager).addCallback(mMediaProjectionCallbackCaptor.capture(), any());
158     }
159 
160     @After
tearDown()161     public void tearDown() {
162         mSensitiveContentProtectionManagerService.onDestroy();
163     }
164 
setupSensitiveNotification()165     private ArraySet<PackageInfo> setupSensitiveNotification() {
166         // Setup Notification Values
167         when(mNotification1.getKey()).thenReturn(NOTIFICATION_KEY_1);
168         when(mNotification1.getPackageName()).thenReturn(NOTIFICATION_PKG_1);
169         when(mNotification1.getUid()).thenReturn(NOTIFICATION_UID_1);
170 
171         when(mNotification2.getKey()).thenReturn(NOTIFICATION_KEY_2);
172         when(mNotification2.getPackageName()).thenReturn(NOTIFICATION_PKG_2);
173         when(mNotification2.getUid()).thenReturn(NOTIFICATION_UID_1);
174 
175         StatusBarNotification[] mNotifications =
176                 new StatusBarNotification[] {mNotification1, mNotification2};
177         doReturn(mNotifications)
178                 .when(mSensitiveContentProtectionManagerService.mNotificationListener)
179                 .getActiveNotifications();
180 
181         when(mRankingMap.getRawRankingObject(eq(NOTIFICATION_KEY_1)))
182                 .thenReturn(mSensitiveRanking);
183         when(mRankingMap.getRawRankingObject(eq(NOTIFICATION_KEY_2)))
184                 .thenReturn(mNonSensitiveRanking);
185 
186         return new ArraySet<>(
187                 Set.of(new PackageInfo(NOTIFICATION_PKG_1, NOTIFICATION_UID_1)));
188     }
189 
setupMultipleSensitiveNotificationsFromSamePackageAndUid()190     private ArraySet<PackageInfo> setupMultipleSensitiveNotificationsFromSamePackageAndUid() {
191         // Setup Notification Values
192         when(mNotification1.getKey()).thenReturn(NOTIFICATION_KEY_1);
193         when(mNotification1.getPackageName()).thenReturn(NOTIFICATION_PKG_1);
194         when(mNotification1.getUid()).thenReturn(NOTIFICATION_UID_1);
195 
196         when(mNotification2.getKey()).thenReturn(NOTIFICATION_KEY_2);
197         when(mNotification2.getPackageName()).thenReturn(NOTIFICATION_PKG_1);
198         when(mNotification2.getUid()).thenReturn(NOTIFICATION_UID_1);
199 
200         StatusBarNotification[] mNotifications =
201                 new StatusBarNotification[] {mNotification1, mNotification2};
202         doReturn(mNotifications)
203                 .when(mSensitiveContentProtectionManagerService.mNotificationListener)
204                 .getActiveNotifications();
205 
206         when(mRankingMap.getRawRankingObject(eq(NOTIFICATION_KEY_1)))
207                 .thenReturn(mSensitiveRanking);
208         when(mRankingMap.getRawRankingObject(eq(NOTIFICATION_KEY_2)))
209                 .thenReturn(mSensitiveRanking);
210 
211         return new ArraySet<>(
212                 Set.of(new PackageInfo(NOTIFICATION_PKG_1, NOTIFICATION_UID_1)));
213     }
214 
setupMultipleSensitiveNotificationsFromDifferentPackage()215     private ArraySet<PackageInfo> setupMultipleSensitiveNotificationsFromDifferentPackage() {
216         // Setup Notification Values
217         when(mNotification1.getKey()).thenReturn(NOTIFICATION_KEY_1);
218         when(mNotification1.getPackageName()).thenReturn(NOTIFICATION_PKG_1);
219         when(mNotification1.getUid()).thenReturn(NOTIFICATION_UID_1);
220 
221         when(mNotification2.getKey()).thenReturn(NOTIFICATION_KEY_2);
222         when(mNotification2.getPackageName()).thenReturn(NOTIFICATION_PKG_2);
223         when(mNotification2.getUid()).thenReturn(NOTIFICATION_UID_1);
224 
225         StatusBarNotification[] mNotifications =
226                 new StatusBarNotification[] {mNotification1, mNotification2};
227         doReturn(mNotifications)
228                 .when(mSensitiveContentProtectionManagerService.mNotificationListener)
229                 .getActiveNotifications();
230 
231         when(mRankingMap.getRawRankingObject(eq(NOTIFICATION_KEY_1)))
232                 .thenReturn(mSensitiveRanking);
233         when(mRankingMap.getRawRankingObject(eq(NOTIFICATION_KEY_2)))
234                 .thenReturn(mSensitiveRanking);
235 
236         return new ArraySet<>(
237                 Set.of(new PackageInfo(NOTIFICATION_PKG_1, NOTIFICATION_UID_1),
238                         new PackageInfo(NOTIFICATION_PKG_2, NOTIFICATION_UID_1)));
239     }
240 
setupMultipleSensitiveNotificationsFromDifferentUid()241     private ArraySet<PackageInfo> setupMultipleSensitiveNotificationsFromDifferentUid() {
242         // Setup Notification Values
243         when(mNotification1.getKey()).thenReturn(NOTIFICATION_KEY_1);
244         when(mNotification1.getPackageName()).thenReturn(NOTIFICATION_PKG_1);
245         when(mNotification1.getUid()).thenReturn(NOTIFICATION_UID_1);
246 
247         when(mNotification2.getKey()).thenReturn(NOTIFICATION_KEY_2);
248         when(mNotification2.getPackageName()).thenReturn(NOTIFICATION_PKG_1);
249         when(mNotification2.getUid()).thenReturn(NOTIFICATION_UID_2);
250 
251         StatusBarNotification[] mNotifications =
252                 new StatusBarNotification[] {mNotification1, mNotification2};
253         doReturn(mNotifications)
254                 .when(mSensitiveContentProtectionManagerService.mNotificationListener)
255                 .getActiveNotifications();
256 
257         when(mRankingMap.getRawRankingObject(eq(NOTIFICATION_KEY_1)))
258                 .thenReturn(mSensitiveRanking);
259         when(mRankingMap.getRawRankingObject(eq(NOTIFICATION_KEY_2)))
260                 .thenReturn(mSensitiveRanking);
261 
262         return new ArraySet<>(
263                 Set.of(new PackageInfo(NOTIFICATION_PKG_1, NOTIFICATION_UID_1),
264                         new PackageInfo(NOTIFICATION_PKG_1, NOTIFICATION_UID_2)));
265     }
266 
setupNoSensitiveNotifications()267     private void setupNoSensitiveNotifications() {
268         // Setup Notification Values
269         when(mNotification1.getKey()).thenReturn(NOTIFICATION_KEY_1);
270         when(mNotification1.getPackageName()).thenReturn(NOTIFICATION_PKG_1);
271         when(mNotification1.getUid()).thenReturn(NOTIFICATION_UID_1);
272 
273         StatusBarNotification[] mNotifications = new StatusBarNotification[] {mNotification1};
274         doReturn(mNotifications)
275                 .when(mSensitiveContentProtectionManagerService.mNotificationListener)
276                 .getActiveNotifications();
277 
278         when(mRankingMap.getRawRankingObject(eq(NOTIFICATION_KEY_1)))
279                 .thenReturn(mNonSensitiveRanking);
280     }
281 
setupNoNotifications()282     private void setupNoNotifications() {
283         // Setup Notification Values
284         StatusBarNotification[] mNotifications = new StatusBarNotification[] {};
285         doReturn(mNotifications)
286                 .when(mSensitiveContentProtectionManagerService.mNotificationListener)
287                 .getActiveNotifications();
288     }
289 
setupNullNotifications()290     private void setupNullNotifications() {
291         // Setup Notification Values
292         StatusBarNotification[] mNotifications = new StatusBarNotification[] { null, null};
293         doReturn(mNotifications)
294                 .when(mSensitiveContentProtectionManagerService.mNotificationListener)
295                 .getActiveNotifications();
296     }
297 
createMediaProjectionInfo()298     private MediaProjectionInfo createMediaProjectionInfo() {
299         return new MediaProjectionInfo(SCREEN_RECORDER_PACKAGE, Process.myUserHandle(), null);
300     }
301 
createExemptMediaProjectionInfo()302     private MediaProjectionInfo createExemptMediaProjectionInfo() {
303         return new MediaProjectionInfo(
304                 EXEMPTED_SCREEN_RECORDER_PACKAGE, Process.myUserHandle(), null);
305     }
306 
307     @Test
mediaProjectionOnStart_verifyExemptedRecorderPackage()308     public void mediaProjectionOnStart_verifyExemptedRecorderPackage() {
309         MediaProjectionInfo mediaProjectionInfo = createExemptMediaProjectionInfo();
310 
311         mMediaProjectionCallbackCaptor.getValue().onStart(mediaProjectionInfo);
312 
313         verify(mWindowManager, never()).addBlockScreenCaptureForApps(mPackageInfoCaptor.capture());
314     }
315 
316     @Test
317     @RequiresFlagsDisabled(FLAG_SENSITIVE_CONTENT_METRICS_BUGFIX)
mediaProjectionOnStart_flagDisabled_neverSetBlockScreenCaptureForAppsSessionId()318     public void mediaProjectionOnStart_flagDisabled_neverSetBlockScreenCaptureForAppsSessionId() {
319         setupSensitiveNotification();
320 
321         mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
322 
323         verify(mWindowManager, never()).setBlockScreenCaptureForAppsSessionId(anyLong());
324     }
325 
326     @Test
327     @RequiresFlagsEnabled(FLAG_SENSITIVE_CONTENT_METRICS_BUGFIX)
mediaProjectionOnStart_setBlockScreenCaptureForAppsSessionId()328     public void mediaProjectionOnStart_setBlockScreenCaptureForAppsSessionId() {
329         setupSensitiveNotification();
330 
331         mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
332 
333         verify(mWindowManager).setBlockScreenCaptureForAppsSessionId(anyLong());
334     }
335 
336     @Test
mediaProjectionOnStart_onProjectionStart_setWmBlockedPackages()337     public void mediaProjectionOnStart_onProjectionStart_setWmBlockedPackages() {
338         ArraySet<PackageInfo> expectedBlockedPackages = setupSensitiveNotification();
339 
340         mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
341 
342         verify(mWindowManager).addBlockScreenCaptureForApps(expectedBlockedPackages);
343     }
344 
345     @Test
mediaProjectionOnStart_noSensitiveNotifications_noBlockedPackages()346     public void mediaProjectionOnStart_noSensitiveNotifications_noBlockedPackages() {
347         setupNoSensitiveNotifications();
348 
349         mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
350 
351         verify(mWindowManager, never()).addBlockScreenCaptureForApps(any());
352     }
353 
354     @Test
mediaProjectionOnStart_noNotifications_noBlockedPackages()355     public void mediaProjectionOnStart_noNotifications_noBlockedPackages() {
356         setupNoNotifications();
357 
358         mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
359 
360         verify(mWindowManager, never()).addBlockScreenCaptureForApps(any());
361     }
362 
363     @Test
mediaProjectionOnStart_multipleNotifications_setWmBlockedPackages()364     public void mediaProjectionOnStart_multipleNotifications_setWmBlockedPackages() {
365         ArraySet<PackageInfo> expectedBlockedPackages =
366                 setupMultipleSensitiveNotificationsFromSamePackageAndUid();
367 
368         mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
369 
370         verify(mWindowManager).addBlockScreenCaptureForApps(expectedBlockedPackages);
371     }
372 
373     @Test
mediaProjectionOnStart_multiplePackages_setWmBlockedPackages()374     public void mediaProjectionOnStart_multiplePackages_setWmBlockedPackages() {
375         ArraySet<PackageInfo> expectedBlockedPackages =
376                 setupMultipleSensitiveNotificationsFromDifferentPackage();
377 
378         mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
379 
380         verify(mWindowManager).addBlockScreenCaptureForApps(expectedBlockedPackages);
381     }
382 
383     @Test
mediaProjectionOnStart_multipleUid_setWmBlockedPackages()384     public void mediaProjectionOnStart_multipleUid_setWmBlockedPackages() {
385         ArraySet<PackageInfo> expectedBlockedPackages =
386                 setupMultipleSensitiveNotificationsFromDifferentUid();
387 
388         mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
389 
390         verify(mWindowManager).addBlockScreenCaptureForApps(expectedBlockedPackages);
391     }
392 
393     @Test
mediaProjectionOnStop_onProjectionEnd_clearWmBlockedPackages()394     public void mediaProjectionOnStop_onProjectionEnd_clearWmBlockedPackages() {
395         setupSensitiveNotification();
396 
397         MediaProjectionInfo mediaProjectionInfo = createMediaProjectionInfo();
398         mMediaProjectionCallbackCaptor.getValue().onStart(mediaProjectionInfo);
399         Mockito.reset(mWindowManager);
400 
401         mMediaProjectionCallbackCaptor.getValue().onStop(mediaProjectionInfo);
402 
403         verify(mWindowManager).clearBlockedApps();
404     }
405 
406     @Test
mediaProjectionOnStart_afterOnStop_onProjectionStart_setWmBlockedPackages()407     public void mediaProjectionOnStart_afterOnStop_onProjectionStart_setWmBlockedPackages() {
408         ArraySet<PackageInfo> expectedBlockedPackages = setupSensitiveNotification();
409 
410         MediaProjectionInfo mediaProjectionInfo = createMediaProjectionInfo();
411         mMediaProjectionCallbackCaptor.getValue().onStart(mediaProjectionInfo);
412         mMediaProjectionCallbackCaptor.getValue().onStop(mediaProjectionInfo);
413         Mockito.reset(mWindowManager);
414 
415         mMediaProjectionCallbackCaptor.getValue().onStart(mediaProjectionInfo);
416 
417         verify(mWindowManager).addBlockScreenCaptureForApps(expectedBlockedPackages);
418     }
419 
420     @Test
mediaProjectionOnStart_getActiveNotificationsThrows_noBlockedPackages()421     public void mediaProjectionOnStart_getActiveNotificationsThrows_noBlockedPackages() {
422         doThrow(SecurityException.class)
423                 .when(mSensitiveContentProtectionManagerService.mNotificationListener)
424                 .getActiveNotifications();
425 
426         mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
427 
428         verify(mWindowManager, never()).addBlockScreenCaptureForApps(any());
429     }
430 
431     @Test
mediaProjectionOnStart_getCurrentRankingThrows_noBlockedPackages()432     public void mediaProjectionOnStart_getCurrentRankingThrows_noBlockedPackages() {
433         doThrow(SecurityException.class)
434                 .when(mSensitiveContentProtectionManagerService.mNotificationListener)
435                 .getCurrentRanking();
436 
437         mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
438 
439         verify(mWindowManager, never()).addBlockScreenCaptureForApps(any());
440     }
441 
442     @Test
mediaProjectionOnStart_getCurrentRanking_nullRankingMap_noBlockedPackages()443     public void mediaProjectionOnStart_getCurrentRanking_nullRankingMap_noBlockedPackages() {
444         doReturn(null)
445                 .when(mSensitiveContentProtectionManagerService.mNotificationListener)
446                 .getCurrentRanking();
447 
448         mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
449 
450         verify(mWindowManager, never()).addBlockScreenCaptureForApps(any());
451     }
452 
453     @Test
mediaProjectionOnStart_getCurrentRanking_missingRanking_noBlockedPackages()454     public void mediaProjectionOnStart_getCurrentRanking_missingRanking_noBlockedPackages() {
455         when(mRankingMap.getRawRankingObject(eq(NOTIFICATION_KEY_1))).thenReturn(null);
456 
457         doReturn(mRankingMap)
458                 .when(mSensitiveContentProtectionManagerService.mNotificationListener)
459                 .getCurrentRanking();
460 
461         mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
462 
463         verify(mWindowManager, never()).addBlockScreenCaptureForApps(any());
464     }
465 
466     @Test
mediaProjectionOnStart_disabledViaDevOption_noBlockedPackages()467     public void mediaProjectionOnStart_disabledViaDevOption_noBlockedPackages() {
468         mockDisabledViaDevelopOption();
469         setupSensitiveNotification();
470 
471         mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
472 
473         verify(mWindowManager, never()).addBlockScreenCaptureForApps(mPackageInfoCaptor.capture());
474     }
475 
476     @Test
nlsOnListenerConnected_projectionNotStarted_noop()477     public void nlsOnListenerConnected_projectionNotStarted_noop() {
478         // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
479         // as non-sensitive
480         setupSensitiveNotification();
481 
482         mSensitiveContentProtectionManagerService.mNotificationListener.onListenerConnected();
483 
484         verify(mWindowManager, never()).addBlockScreenCaptureForApps(mPackageInfoCaptor.capture());
485     }
486 
487     @Test
nlsOnListenerConnected_projectionStopped_noop()488     public void nlsOnListenerConnected_projectionStopped_noop() {
489         // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
490         // as non-sensitive
491         setupSensitiveNotification();
492         MediaProjectionInfo mediaProjectionInfo = createMediaProjectionInfo();
493         mMediaProjectionCallbackCaptor.getValue().onStart(mediaProjectionInfo);
494         mMediaProjectionCallbackCaptor.getValue().onStop(mediaProjectionInfo);
495         Mockito.reset(mWindowManager);
496 
497         mSensitiveContentProtectionManagerService.mNotificationListener.onListenerConnected();
498 
499         verify(mWindowManager, never()).addBlockScreenCaptureForApps(mPackageInfoCaptor.capture());
500     }
501 
502     @Test
nlsOnListenerConnected_projectionStarted_setWmBlockedPackages()503     public void nlsOnListenerConnected_projectionStarted_setWmBlockedPackages() {
504         // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
505         // as non-sensitive
506         ArraySet<PackageInfo> expectedBlockedPackages = setupSensitiveNotification();
507         mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
508         Mockito.reset(mWindowManager);
509 
510         mSensitiveContentProtectionManagerService.mNotificationListener.onListenerConnected();
511 
512         verify(mWindowManager).addBlockScreenCaptureForApps(expectedBlockedPackages);
513     }
514 
515     @Test
nlsOnListenerConnected_noSensitiveNotifications_noBlockedPackages()516     public void nlsOnListenerConnected_noSensitiveNotifications_noBlockedPackages() {
517         setupNoSensitiveNotifications();
518         mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
519         Mockito.reset(mWindowManager);
520 
521         mSensitiveContentProtectionManagerService.mNotificationListener.onListenerConnected();
522 
523         verify(mWindowManager, never()).addBlockScreenCaptureForApps(mPackageInfoCaptor.capture());
524     }
525 
526     @Test
nlsOnListenerConnected_noNotifications_noBlockedPackages()527     public void nlsOnListenerConnected_noNotifications_noBlockedPackages() {
528         setupNoNotifications();
529         mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
530         Mockito.reset(mWindowManager);
531 
532         mSensitiveContentProtectionManagerService.mNotificationListener.onListenerConnected();
533 
534         verify(mWindowManager, never()).addBlockScreenCaptureForApps(mPackageInfoCaptor.capture());
535     }
536 
537     @Test
nlsOnListenerConnected_nullNotifications_noBlockedPackages()538     public void nlsOnListenerConnected_nullNotifications_noBlockedPackages() {
539         setupNullNotifications();
540         mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
541         Mockito.reset(mWindowManager);
542 
543         mSensitiveContentProtectionManagerService.mNotificationListener.onListenerConnected();
544 
545         verify(mWindowManager, never()).addBlockScreenCaptureForApps(mPackageInfoCaptor.capture());
546     }
547 
548     @Test
nlsOnListenerConnected_nullRankingMap_noBlockedPackages()549     public void nlsOnListenerConnected_nullRankingMap_noBlockedPackages() {
550         // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
551         // as non-sensitive
552         setupSensitiveNotification();
553         mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
554         Mockito.reset(mWindowManager);
555         doReturn(null)
556                 .when(mSensitiveContentProtectionManagerService.mNotificationListener)
557                 .getCurrentRanking();
558 
559         mSensitiveContentProtectionManagerService.mNotificationListener.onListenerConnected();
560 
561         verify(mWindowManager, never()).addBlockScreenCaptureForApps(mPackageInfoCaptor.capture());
562     }
563 
564     @Test
nlsOnListenerConnected_missingRanking_noBlockedPackages()565     public void nlsOnListenerConnected_missingRanking_noBlockedPackages() {
566         // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
567         // as non-sensitive
568         setupSensitiveNotification();
569         mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
570         Mockito.reset(mWindowManager);
571         when(mRankingMap.getRawRankingObject(eq(NOTIFICATION_KEY_1))).thenReturn(null);
572         doReturn(mRankingMap)
573                 .when(mSensitiveContentProtectionManagerService.mNotificationListener)
574                 .getCurrentRanking();
575 
576         mSensitiveContentProtectionManagerService.mNotificationListener.onListenerConnected();
577 
578         verify(mWindowManager, never()).addBlockScreenCaptureForApps(mPackageInfoCaptor.capture());
579     }
580 
581     @Test
nlsOnListenerConnected_disabledViaDevOption_noBlockedPackages()582     public void nlsOnListenerConnected_disabledViaDevOption_noBlockedPackages() {
583         mockDisabledViaDevelopOption();
584         // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
585         // as non-sensitive
586         setupSensitiveNotification();
587         mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
588         mSensitiveContentProtectionManagerService.mNotificationListener.onListenerConnected();
589 
590         verify(mWindowManager, never()).addBlockScreenCaptureForApps(mPackageInfoCaptor.capture());
591     }
592 
593     @Test
nlsOnNotificationRankingUpdate_projectionNotStarted_noop()594     public void nlsOnNotificationRankingUpdate_projectionNotStarted_noop() {
595         // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
596         // as non-sensitive
597         setupSensitiveNotification();
598 
599         mSensitiveContentProtectionManagerService.mNotificationListener
600                 .onNotificationRankingUpdate(mRankingMap);
601 
602         verifyNoBlockOrClearInteractionWithWindowManager();
603     }
604 
605     @Test
nlsOnNotificationRankingUpdate_projectionStopped_noop()606     public void nlsOnNotificationRankingUpdate_projectionStopped_noop() {
607         // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
608         // as non-sensitive
609         setupSensitiveNotification();
610         MediaProjectionInfo mediaProjectionInfo = createMediaProjectionInfo();
611         mMediaProjectionCallbackCaptor.getValue().onStart(mediaProjectionInfo);
612         mMediaProjectionCallbackCaptor.getValue().onStop(mediaProjectionInfo);
613         Mockito.reset(mWindowManager);
614 
615         mSensitiveContentProtectionManagerService.mNotificationListener
616                 .onNotificationRankingUpdate(mRankingMap);
617 
618         verifyNoBlockOrClearInteractionWithWindowManager();
619     }
620 
621     @Test
nlsOnNotificationRankingUpdate_projectionStarted_setWmBlockedPackages()622     public void nlsOnNotificationRankingUpdate_projectionStarted_setWmBlockedPackages() {
623         // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
624         // as non-sensitive
625         ArraySet<PackageInfo> expectedBlockedPackages = setupSensitiveNotification();
626         mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
627         Mockito.reset(mWindowManager);
628 
629         mSensitiveContentProtectionManagerService.mNotificationListener
630                 .onNotificationRankingUpdate(mRankingMap);
631 
632         verify(mWindowManager).addBlockScreenCaptureForApps(expectedBlockedPackages);
633     }
634 
635     @Test
nlsOnNotificationRankingUpdate_noSensitiveNotifications_noBlockedPackages()636     public void nlsOnNotificationRankingUpdate_noSensitiveNotifications_noBlockedPackages() {
637         setupNoSensitiveNotifications();
638         mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
639         Mockito.reset(mWindowManager);
640 
641         mSensitiveContentProtectionManagerService.mNotificationListener
642                 .onNotificationRankingUpdate(mRankingMap);
643 
644         verify(mWindowManager, never()).addBlockScreenCaptureForApps(mPackageInfoCaptor.capture());
645     }
646 
647     @Test
nlsOnNotificationRankingUpdate_noNotifications_noBlockedPackages()648     public void nlsOnNotificationRankingUpdate_noNotifications_noBlockedPackages() {
649         setupNoNotifications();
650         mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
651         Mockito.reset(mWindowManager);
652 
653         mSensitiveContentProtectionManagerService.mNotificationListener
654                 .onNotificationRankingUpdate(mRankingMap);
655 
656         verify(mWindowManager, never()).addBlockScreenCaptureForApps(mPackageInfoCaptor.capture());
657     }
658 
659     @Test
nlsOnNotificationRankingUpdate_nullRankingMap_noBlockedPackages()660     public void nlsOnNotificationRankingUpdate_nullRankingMap_noBlockedPackages() {
661         // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
662         // as non-sensitive
663         setupSensitiveNotification();
664         mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
665         Mockito.reset(mWindowManager);
666 
667         mSensitiveContentProtectionManagerService.mNotificationListener
668                 .onNotificationRankingUpdate(null);
669 
670         verify(mWindowManager, never()).addBlockScreenCaptureForApps(mPackageInfoCaptor.capture());
671     }
672 
673     @Test
nlsOnNotificationRankingUpdate_missingRanking_noBlockedPackages()674     public void nlsOnNotificationRankingUpdate_missingRanking_noBlockedPackages() {
675         // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
676         // as non-sensitive
677         setupSensitiveNotification();
678         mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
679         Mockito.reset(mWindowManager);
680         when(mRankingMap.getRawRankingObject(eq(NOTIFICATION_KEY_1))).thenReturn(null);
681         doReturn(mRankingMap)
682                 .when(mSensitiveContentProtectionManagerService.mNotificationListener)
683                 .getCurrentRanking();
684 
685         mSensitiveContentProtectionManagerService.mNotificationListener
686                 .onNotificationRankingUpdate(mRankingMap);
687 
688         verify(mWindowManager, never()).addBlockScreenCaptureForApps(mPackageInfoCaptor.capture());
689     }
690 
691     @Test
nlsOnNotificationRankingUpdate_getActiveNotificationsThrows_noBlockedPackages()692     public void nlsOnNotificationRankingUpdate_getActiveNotificationsThrows_noBlockedPackages() {
693         // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
694         // as non-sensitive
695         setupSensitiveNotification();
696         mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
697         Mockito.reset(mWindowManager);
698 
699         doThrow(SecurityException.class)
700                 .when(mSensitiveContentProtectionManagerService.mNotificationListener)
701                 .getActiveNotifications();
702 
703         mSensitiveContentProtectionManagerService.mNotificationListener
704                 .onNotificationRankingUpdate(mRankingMap);
705 
706         verify(mWindowManager, never()).addBlockScreenCaptureForApps(mPackageInfoCaptor.capture());
707     }
708 
709     @Test
nlsOnNotificationRankingUpdate_disabledViaDevOption_noBlockedPackages()710     public void nlsOnNotificationRankingUpdate_disabledViaDevOption_noBlockedPackages() {
711         mockDisabledViaDevelopOption();
712         // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
713         // as non-sensitive
714         setupSensitiveNotification();
715         mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
716         mSensitiveContentProtectionManagerService.mNotificationListener
717                 .onNotificationRankingUpdate(mRankingMap);
718 
719         verify(mWindowManager, never()).addBlockScreenCaptureForApps(mPackageInfoCaptor.capture());
720     }
721 
722     @Test
nlsOnNotificationPosted_projectionNotStarted_noop()723     public void nlsOnNotificationPosted_projectionNotStarted_noop() {
724         // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
725         // as non-sensitive
726         setupSensitiveNotification();
727 
728         mSensitiveContentProtectionManagerService.mNotificationListener
729                 .onNotificationPosted(mNotification1, mRankingMap);
730 
731         verifyNoBlockOrClearInteractionWithWindowManager();
732     }
733 
734     @Test
nlsOnNotificationPosted_projectionStopped_noop()735     public void nlsOnNotificationPosted_projectionStopped_noop() {
736         // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
737         // as non-sensitive
738         setupSensitiveNotification();
739         MediaProjectionInfo mediaProjectionInfo = createMediaProjectionInfo();
740         mMediaProjectionCallbackCaptor.getValue().onStart(mediaProjectionInfo);
741         mMediaProjectionCallbackCaptor.getValue().onStop(mediaProjectionInfo);
742         Mockito.reset(mWindowManager);
743 
744         mSensitiveContentProtectionManagerService.mNotificationListener
745                 .onNotificationPosted(mNotification1, mRankingMap);
746 
747         verifyNoBlockOrClearInteractionWithWindowManager();
748     }
749 
750     @Test
nlsOnNotificationPosted_projectionStarted_setWmBlockedPackages()751     public void nlsOnNotificationPosted_projectionStarted_setWmBlockedPackages() {
752         // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
753         // as non-sensitive
754         setupSensitiveNotification();
755         mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
756         Mockito.reset(mWindowManager);
757 
758         mSensitiveContentProtectionManagerService.mNotificationListener
759                 .onNotificationPosted(mNotification1, mRankingMap);
760 
761         ArraySet<PackageInfo> expectedBlockedPackages = new ArraySet<>(
762                 Set.of(new PackageInfo(NOTIFICATION_PKG_1, NOTIFICATION_UID_1)));
763         verify(mWindowManager).addBlockScreenCaptureForApps(expectedBlockedPackages);
764     }
765 
766     @Test
nlsOnNotificationPosted_noSensitiveNotifications_noBlockedPackages()767     public void nlsOnNotificationPosted_noSensitiveNotifications_noBlockedPackages() {
768         // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
769         // as non-sensitive
770         setupSensitiveNotification();
771         mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
772         Mockito.reset(mWindowManager);
773 
774         mSensitiveContentProtectionManagerService.mNotificationListener
775                 .onNotificationPosted(mNotification2, mRankingMap);
776 
777         verify(mWindowManager, never()).addBlockScreenCaptureForApps(mPackageInfoCaptor.capture());
778     }
779 
780     @Test
nlsOnNotificationPosted_noNotifications_noBlockedPackages()781     public void nlsOnNotificationPosted_noNotifications_noBlockedPackages() {
782         // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
783         // as non-sensitive
784         setupSensitiveNotification();
785         mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
786         Mockito.reset(mWindowManager);
787 
788         mSensitiveContentProtectionManagerService.mNotificationListener
789                 .onNotificationPosted(null, mRankingMap);
790 
791         verify(mWindowManager, never()).addBlockScreenCaptureForApps(mPackageInfoCaptor.capture());
792     }
793 
794     @Test
nlsOnNotificationPosted_nullRankingMap_noBlockedPackages()795     public void nlsOnNotificationPosted_nullRankingMap_noBlockedPackages() {
796         // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
797         // as non-sensitive
798         setupSensitiveNotification();
799         mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
800         Mockito.reset(mWindowManager);
801 
802         mSensitiveContentProtectionManagerService.mNotificationListener
803                 .onNotificationPosted(mNotification1, null);
804 
805         verify(mWindowManager, never()).addBlockScreenCaptureForApps(mPackageInfoCaptor.capture());
806     }
807 
808     @Test
nlsOnNotificationPosted_missingRanking_noBlockedPackages()809     public void nlsOnNotificationPosted_missingRanking_noBlockedPackages() {
810         // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
811         // as non-sensitive
812         setupSensitiveNotification();
813         mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
814         Mockito.reset(mWindowManager);
815         when(mRankingMap.getRawRankingObject(eq(NOTIFICATION_KEY_1))).thenReturn(null);
816 
817         mSensitiveContentProtectionManagerService.mNotificationListener
818                 .onNotificationPosted(mNotification1, mRankingMap);
819 
820         verify(mWindowManager, never()).addBlockScreenCaptureForApps(mPackageInfoCaptor.capture());
821     }
822 
823     @Test
nlsOnNotificationPosted_disabledViaDevOption_noBlockedPackages()824     public void nlsOnNotificationPosted_disabledViaDevOption_noBlockedPackages() {
825         mockDisabledViaDevelopOption();
826         // Sets up mNotification1 & mRankingMap to be a sensitive notification, and mNotification2
827         // as non-sensitive
828         setupSensitiveNotification();
829         mMediaProjectionCallbackCaptor.getValue().onStart(createMediaProjectionInfo());
830         mSensitiveContentProtectionManagerService.mNotificationListener
831                 .onNotificationPosted(mNotification1, mRankingMap);
832 
833         verify(mWindowManager, never()).addBlockScreenCaptureForApps(mPackageInfoCaptor.capture());
834     }
835 
verifyNoBlockOrClearInteractionWithWindowManager()836     private void verifyNoBlockOrClearInteractionWithWindowManager() {
837         verify(mWindowManager, never()).addBlockScreenCaptureForApps(mPackageInfoCaptor.capture());
838         verify(mWindowManager, never()).clearBlockedApps();
839         verify(mWindowManager, never())
840                 .removeBlockScreenCaptureForApps(mPackageInfoCaptor.capture());
841     }
842 
mockDisabledViaDevelopOption()843     private void mockDisabledViaDevelopOption() {
844         // mContext (TestableContext) uses [TestableSettingsProvider] and it will be cleared after
845         // the test
846         Settings.Global.putInt(
847                 mContext.getContentResolver(),
848                 Settings.Global.DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS,
849                 1);
850     }
851 }
852