1 /*
2  * Copyright (C) 2024 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.view.flags.Flags.FLAG_SENSITIVE_CONTENT_APP_PROTECTION;
20 
21 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
22 
23 import static com.google.common.truth.Truth.assertThat;
24 
25 import static org.mockito.ArgumentMatchers.any;
26 import static org.mockito.Mockito.atLeast;
27 import static org.mockito.Mockito.never;
28 import static org.mockito.Mockito.verify;
29 
30 import android.content.pm.PackageManagerInternal;
31 import android.media.projection.MediaProjectionInfo;
32 import android.media.projection.MediaProjectionManager;
33 import android.os.Binder;
34 import android.os.Process;
35 import android.platform.test.annotations.RequiresFlagsEnabled;
36 import android.platform.test.flag.junit.CheckFlagsRule;
37 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
38 import android.provider.Settings;
39 import android.testing.AndroidTestingRunner;
40 import android.testing.TestableContext;
41 import android.util.ArraySet;
42 
43 import androidx.test.filters.SmallTest;
44 
45 import com.android.server.wm.SensitiveContentPackages.PackageInfo;
46 import com.android.server.wm.WindowManagerInternal;
47 
48 import org.junit.Before;
49 import org.junit.Rule;
50 import org.junit.Test;
51 import org.junit.runner.RunWith;
52 import org.mockito.ArgumentCaptor;
53 import org.mockito.Captor;
54 import org.mockito.Mock;
55 import org.mockito.MockitoAnnotations;
56 
57 import java.util.Set;
58 
59 @SmallTest
60 @RunWith(AndroidTestingRunner.class)
61 @RequiresFlagsEnabled(FLAG_SENSITIVE_CONTENT_APP_PROTECTION)
62 /**
63  * Test {@link SensitiveContentProtectionManagerService} for sensitive on screen content
64  * protection, the service protects sensitive content during screen share.
65  */
66 public class SensitiveContentProtectionManagerServiceContentTest {
67     private final PackageInfo mPackageInfo =
68             new PackageInfo("test.package", 12345, new Binder());
69     private final String mScreenRecorderPackage = "test.screen.recorder.package";
70     private final String mExemptedScreenRecorderPackage = "test.exempted.screen.recorder.package";
71     private SensitiveContentProtectionManagerService mSensitiveContentProtectionManagerService;
72     private MediaProjectionManager.Callback mMediaPorjectionCallback;
73 
74     @Mock private WindowManagerInternal mWindowManager;
75     @Mock private MediaProjectionManager mProjectionManager;
76     @Mock private PackageManagerInternal mPackageManagerInternal;
77     private MediaProjectionInfo mMediaProjectionInfo;
78 
79     @Captor
80     private ArgumentCaptor<MediaProjectionManager.Callback> mMediaProjectionCallbackCaptor;
81     @Captor
82     private ArgumentCaptor<ArraySet<PackageInfo>> mPackageInfoCaptor;
83 
84     @Rule
85     public final TestableContext mContext =
86             new TestableContext(getInstrumentation().getTargetContext(), null);
87     @Rule
88     public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
89 
90     @Before
setUp()91     public void setUp() {
92         MockitoAnnotations.initMocks(this);
93         mSensitiveContentProtectionManagerService =
94                 new SensitiveContentProtectionManagerService(mContext);
95         mSensitiveContentProtectionManagerService.init(mProjectionManager, mWindowManager,
96                 mPackageManagerInternal, new ArraySet<>(Set.of(mExemptedScreenRecorderPackage)));
97         verify(mProjectionManager).addCallback(mMediaProjectionCallbackCaptor.capture(), any());
98         mMediaPorjectionCallback = mMediaProjectionCallbackCaptor.getValue();
99         mMediaProjectionInfo =
100                 new MediaProjectionInfo(mScreenRecorderPackage, Process.myUserHandle(), null);
101     }
102 
103     @Test
testExemptedRecorderPackageForScreenCapture()104     public void testExemptedRecorderPackageForScreenCapture() {
105         MediaProjectionInfo exemptedRecorderPackage = new MediaProjectionInfo(
106                 mExemptedScreenRecorderPackage, Process.myUserHandle(), null);
107         mMediaPorjectionCallback.onStart(exemptedRecorderPackage);
108         mSensitiveContentProtectionManagerService.setSensitiveContentProtection(
109                 mPackageInfo.getWindowToken(), mPackageInfo.getPkg(), mPackageInfo.getUid(), true);
110         verify(mWindowManager, never()).addBlockScreenCaptureForApps(mPackageInfoCaptor.capture());
111     }
112 
113     @Test
testBlockAppWindowForScreenCapture()114     public void testBlockAppWindowForScreenCapture() {
115         mMediaPorjectionCallback.onStart(mMediaProjectionInfo);
116         mSensitiveContentProtectionManagerService.setSensitiveContentProtection(
117                 mPackageInfo.getWindowToken(), mPackageInfo.getPkg(), mPackageInfo.getUid(), true);
118         verify(mWindowManager, atLeast(1))
119                 .addBlockScreenCaptureForApps(mPackageInfoCaptor.capture());
120         assertThat(mPackageInfoCaptor.getValue()).containsExactly(mPackageInfo);
121     }
122 
123     @Test
testUnblockAppWindowForScreenCapture()124     public void testUnblockAppWindowForScreenCapture() {
125         mMediaPorjectionCallback.onStart(mMediaProjectionInfo);
126         mSensitiveContentProtectionManagerService.setSensitiveContentProtection(
127                 mPackageInfo.getWindowToken(), mPackageInfo.getPkg(), mPackageInfo.getUid(), false);
128         verify(mWindowManager).removeBlockScreenCaptureForApps(mPackageInfoCaptor.capture());
129         assertThat(mPackageInfoCaptor.getValue()).containsExactly(mPackageInfo);
130     }
131 
132     @Test
testAppWindowIsUnblockedBeforeScreenCapture()133     public void testAppWindowIsUnblockedBeforeScreenCapture() {
134         // when screen sharing is not active, no app window should be blocked.
135         mSensitiveContentProtectionManagerService.setSensitiveContentProtection(
136                 mPackageInfo.getWindowToken(), mPackageInfo.getPkg(), mPackageInfo.getUid(), true);
137         verify(mWindowManager, never()).addBlockScreenCaptureForApps(mPackageInfoCaptor.capture());
138     }
139 
140     @Test
testAppWindowsAreUnblockedOnScreenCaptureEnd()141     public void testAppWindowsAreUnblockedOnScreenCaptureEnd() {
142         mMediaPorjectionCallback.onStart(mMediaProjectionInfo);
143         mSensitiveContentProtectionManagerService.setSensitiveContentProtection(
144                 mPackageInfo.getWindowToken(), mPackageInfo.getPkg(), mPackageInfo.getUid(), true);
145         // when screen sharing ends, all blocked app windows should be cleared.
146         mMediaPorjectionCallback.onStop(mMediaProjectionInfo);
147         verify(mWindowManager).clearBlockedApps();
148     }
149 
150     @Test
testAutofillServicePackageExemption()151     public void testAutofillServicePackageExemption() {
152         String testAutofillService = mScreenRecorderPackage + "/com.example.SampleAutofillService";
153         int userId = Process.myUserHandle().getIdentifier();
154         Settings.Secure.putStringForUser(mContext.getContentResolver(),
155                 Settings.Secure.AUTOFILL_SERVICE, testAutofillService , userId);
156 
157         mMediaPorjectionCallback.onStart(mMediaProjectionInfo);
158         mSensitiveContentProtectionManagerService.setSensitiveContentProtection(
159                 mPackageInfo.getWindowToken(), mPackageInfo.getPkg(), mPackageInfo.getUid(), true);
160         verify(mWindowManager, never()).addBlockScreenCaptureForApps(mPackageInfoCaptor.capture());
161     }
162 
163     @Test
testDeveloperOptionDisableFeature()164     public void testDeveloperOptionDisableFeature() {
165         mockDisabledViaDeveloperOption();
166         mMediaProjectionCallbackCaptor.getValue().onStart(mMediaProjectionInfo);
167         mSensitiveContentProtectionManagerService.setSensitiveContentProtection(
168                 mPackageInfo.getWindowToken(), mPackageInfo.getPkg(), mPackageInfo.getUid(), true);
169         verify(mWindowManager, never()).addBlockScreenCaptureForApps(mPackageInfoCaptor.capture());
170     }
171 
mockDisabledViaDeveloperOption()172     private void mockDisabledViaDeveloperOption() {
173         Settings.Global.putInt(
174                 mContext.getContentResolver(),
175                 Settings.Global.DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS,
176                 1);
177     }
178 }
179