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