Merge "Expose binder for sensitive content protection" into main
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 13c3ede..7505372 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -6559,6 +6559,13 @@
public static final String ECM_ENHANCED_CONFIRMATION_SERVICE = "ecm_enhanced_confirmation";
/**
+ * Service to protect sensitive content during screen share.
+ * @hide
+ */
+ public static final String SENSITIVE_CONTENT_PROTECTION_SERVICE =
+ "sensitive_content_protection_service";
+
+ /**
* Use with {@link #getSystemService(String)} to retrieve a
* {@link android.provider.ContactKeysManager} to managing contact keys.
*
diff --git a/core/java/android/view/ISensitiveContentProtectionManager.aidl b/core/java/android/view/ISensitiveContentProtectionManager.aidl
new file mode 100644
index 0000000..c135ae4
--- /dev/null
+++ b/core/java/android/view/ISensitiveContentProtectionManager.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.os.IBinder;
+
+/**
+ * @hide
+ */
+oneway interface ISensitiveContentProtectionManager {
+ /**
+ * Block projection for a package's window when the window is showing sensitive content on
+ * the screen, the projection is unblocked when the window no more shows sensitive content.
+ *
+ * @param windowToken window where the content is shown.
+ * @param packageName package name.
+ * @param isShowingSensitiveContent whether the window is showing sensitive content.
+ */
+ void setSensitiveContentProtection(in IBinder windowToken, in String packageName,
+ in boolean isShowingSensitiveContent);
+}
diff --git a/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java b/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java
index fff283d..0904c47 100644
--- a/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java
+++ b/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java
@@ -18,14 +18,17 @@
import static android.permission.flags.Flags.sensitiveNotificationAppProtection;
import static android.provider.Settings.Global.DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS;
-import static com.android.internal.util.Preconditions.checkNotNull;
+import static android.view.flags.Flags.sensitiveContentAppProtection;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageManagerInternal;
import android.media.projection.MediaProjectionInfo;
import android.media.projection.MediaProjectionManager;
+import android.os.Binder;
+import android.os.IBinder;
import android.os.RemoteException;
import android.os.Trace;
import android.os.UserHandle;
@@ -35,29 +38,30 @@
import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
import android.util.Log;
+import android.view.ISensitiveContentProtectionManager;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.wm.SensitiveContentPackages.PackageInfo;
import com.android.server.wm.WindowManagerInternal;
+import java.util.Objects;
import java.util.Set;
/**
- * Service that monitors for notifications with sensitive content and protects content from screen
- * sharing
+ * This service protects sensitive content from screen sharing. The service monitors notifications
+ * for sensitive content and protects from screen share. The service also protects sensitive
+ * content rendered on screen during screen share.
*/
public final class SensitiveContentProtectionManagerService extends SystemService {
private static final String TAG = "SensitiveContentProtect";
private static final boolean DEBUG = false;
- private static final boolean sNotificationProtectionEnabled =
- sensitiveNotificationAppProtection();
@VisibleForTesting
@Nullable
NotificationListener mNotificationListener;
- private @Nullable MediaProjectionManager mProjectionManager;
- private @Nullable WindowManagerInternal mWindowManager;
+ @Nullable private MediaProjectionManager mProjectionManager;
+ @Nullable private WindowManagerInternal mWindowManager;
final Object mSensitiveContentProtectionLock = new Object();
@@ -92,7 +96,7 @@
public SensitiveContentProtectionManagerService(@NonNull Context context) {
super(context);
- if (sNotificationProtectionEnabled) {
+ if (sensitiveNotificationAppProtection()) {
mNotificationListener = new NotificationListener();
}
}
@@ -110,14 +114,18 @@
init(getContext().getSystemService(MediaProjectionManager.class),
LocalServices.getService(WindowManagerInternal.class));
+ if (sensitiveContentAppProtection()) {
+ publishBinderService(Context.SENSITIVE_CONTENT_PROTECTION_SERVICE,
+ new SensitiveContentProtectionManagerServiceBinder());
+ }
}
@VisibleForTesting
void init(MediaProjectionManager projectionManager, WindowManagerInternal windowManager) {
if (DEBUG) Log.d(TAG, "init");
- checkNotNull(projectionManager, "Failed to get valid MediaProjectionManager");
- checkNotNull(windowManager, "Failed to get valid WindowManagerInternal");
+ Objects.requireNonNull(projectionManager);
+ Objects.requireNonNull(windowManager);
mProjectionManager = projectionManager;
mWindowManager = windowManager;
@@ -126,7 +134,7 @@
// handler, delegate, and binder death recipient
mProjectionManager.addCallback(mProjectionCallback, getContext().getMainThreadHandler());
- if (sNotificationProtectionEnabled) {
+ if (sensitiveNotificationAppProtection()) {
try {
mNotificationListener.registerAsSystemService(
getContext(),
@@ -144,7 +152,7 @@
if (mProjectionManager != null) {
mProjectionManager.removeCallback(mProjectionCallback);
}
- if (sNotificationProtectionEnabled) {
+ if (sensitiveNotificationAppProtection()) {
try {
mNotificationListener.unregisterAsSystemService();
} catch (RemoteException e) {
@@ -169,7 +177,7 @@
synchronized (mSensitiveContentProtectionLock) {
mProjectionActive = true;
- if (sNotificationProtectionEnabled) {
+ if (sensitiveNotificationAppProtection()) {
updateAppsThatShouldBlockScreenCapture();
}
}
@@ -305,4 +313,74 @@
}
}
}
+
+ /**
+ * Block projection for a package window when the window is showing sensitive content on
+ * the screen, the projection is unblocked when window no more shows sensitive content.
+ *
+ * @param windowToken window where the content is shown.
+ * @param packageName package name.
+ * @param uid uid of the package.
+ * @param isShowingSensitiveContent whether the window is showing sensitive content.
+ */
+ @VisibleForTesting
+ void setSensitiveContentProtection(IBinder windowToken, String packageName, int uid,
+ boolean isShowingSensitiveContent) {
+ synchronized (mSensitiveContentProtectionLock) {
+ if (!mProjectionActive) {
+ return;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "setSensitiveContentProtection - windowToken=" + windowToken
+ + ", package=" + packageName + ", uid=" + uid
+ + ", isShowingSensitiveContent=" + isShowingSensitiveContent);
+ }
+
+ // The window token distinguish this package from packages added for notifications.
+ PackageInfo packageInfo = new PackageInfo(packageName, uid, windowToken);
+ ArraySet<PackageInfo> packageInfos = new ArraySet<>();
+ packageInfos.add(packageInfo);
+ if (isShowingSensitiveContent) {
+ mWindowManager.addBlockScreenCaptureForApps(packageInfos);
+ } else {
+ mWindowManager.removeBlockScreenCaptureForApps(packageInfos);
+ }
+ }
+ }
+
+ private final class SensitiveContentProtectionManagerServiceBinder
+ extends ISensitiveContentProtectionManager.Stub {
+ private final PackageManagerInternal mPackageManagerInternal;
+
+ SensitiveContentProtectionManagerServiceBinder() {
+ mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+ }
+
+ public void setSensitiveContentProtection(IBinder windowToken, String packageName,
+ boolean isShowingSensitiveContent) {
+ Trace.beginSection(
+ "SensitiveContentProtectionManagerService.setSensitiveContentProtection");
+ try {
+ int callingUid = Binder.getCallingUid();
+ verifyCallingPackage(callingUid, packageName);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ SensitiveContentProtectionManagerService.this.setSensitiveContentProtection(
+ windowToken, packageName, callingUid, isShowingSensitiveContent);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ } finally {
+ Trace.endSection();
+ }
+ }
+
+ private void verifyCallingPackage(int callingUid, String callingPackage) {
+ if (mPackageManagerInternal.getPackageUid(
+ callingPackage, 0, UserHandle.getUserId(callingUid)) != callingUid) {
+ throw new SecurityException("Specified calling package [" + callingPackage
+ + "] does not match the calling uid " + callingUid);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/SensitiveContentPackages.java b/services/core/java/com/android/server/wm/SensitiveContentPackages.java
index 5fe48d1..9ab11b8 100644
--- a/services/core/java/com/android/server/wm/SensitiveContentPackages.java
+++ b/services/core/java/com/android/server/wm/SensitiveContentPackages.java
@@ -16,9 +16,16 @@
package com.android.server.wm;
+import static android.permission.flags.Flags.sensitiveNotificationAppProtection;
+import static android.view.flags.Flags.sensitiveContentAppProtection;
+
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.IBinder;
import android.util.ArraySet;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.io.PrintWriter;
import java.util.Objects;
@@ -29,12 +36,26 @@
public class SensitiveContentPackages {
private final ArraySet<PackageInfo> mProtectedPackages = new ArraySet<>();
- /** Returns {@code true} if package/uid pair should be blocked from screen capture */
- public boolean shouldBlockScreenCaptureForApp(String pkg, int uid) {
+ /**
+ * Returns {@code true} if package/uid/window combination should be blocked
+ * from screen capture.
+ */
+ public boolean shouldBlockScreenCaptureForApp(String pkg, int uid, IBinder windowToken) {
+ if (!(sensitiveContentAppProtection() || sensitiveNotificationAppProtection())) {
+ return false;
+ }
+
for (int i = 0; i < mProtectedPackages.size(); i++) {
PackageInfo info = mProtectedPackages.valueAt(i);
if (info != null && info.mPkg.equals(pkg) && info.mUid == uid) {
- return true;
+ // sensitiveContentAppProtection blocks specific window where sensitive content
+ // is rendered, whereas sensitiveNotificationAppProtection blocks the package
+ // if the package has a sensitive notification.
+ if ((sensitiveContentAppProtection() && windowToken == info.getWindowToken())
+ || (sensitiveNotificationAppProtection() && info.getWindowToken() == null)
+ ) {
+ return true;
+ }
}
}
return false;
@@ -73,31 +94,50 @@
*/
public boolean clearBlockedApps() {
if (mProtectedPackages.isEmpty()) {
- // set was already empty
return false;
}
mProtectedPackages.clear();
return true;
}
+ /**
+ * @return the size of protected packages.
+ */
+ @VisibleForTesting
+ public int size() {
+ return mProtectedPackages.size();
+ }
+
void dump(PrintWriter pw) {
final String innerPrefix = " ";
pw.println("SensitiveContentPackages:");
pw.println(innerPrefix + "Packages that should block screen capture ("
+ mProtectedPackages.size() + "):");
for (PackageInfo info : mProtectedPackages) {
- pw.println(innerPrefix + " package=" + info.mPkg + " uid=" + info.mUid);
+ pw.println(innerPrefix + " package=" + info.mPkg + " uid=" + info.mUid
+ + " windowToken=" + info.mWindowToken);
}
}
- /** Helper class that represents a package/uid pair */
+ /**
+ * Helper class that represents a package, uid, and window token combination, window token
+ * is set to block screen capture at window level.
+ */
public static class PackageInfo {
- private String mPkg;
- private int mUid;
+ private final String mPkg;
+ private final int mUid;
+
+ @Nullable
+ private final IBinder mWindowToken;
public PackageInfo(String pkg, int uid) {
+ this(pkg, uid, null);
+ }
+
+ public PackageInfo(String pkg, int uid, IBinder windowToken) {
this.mPkg = pkg;
this.mUid = uid;
+ this.mWindowToken = windowToken;
}
@Override
@@ -105,12 +145,30 @@
if (this == o) return true;
if (!(o instanceof PackageInfo)) return false;
PackageInfo that = (PackageInfo) o;
- return mUid == that.mUid && Objects.equals(mPkg, that.mPkg);
+ return mUid == that.mUid && Objects.equals(mPkg, that.mPkg)
+ && Objects.equals(mWindowToken, that.mWindowToken);
}
@Override
public int hashCode() {
- return Objects.hash(mPkg, mUid);
+ return Objects.hash(mPkg, mUid, mWindowToken);
+ }
+
+ public IBinder getWindowToken() {
+ return mWindowToken;
+ }
+
+ public int getUid() {
+ return mUid;
+ }
+
+ public String getPkg() {
+ return mPkg;
+ }
+
+ @Override
+ public String toString() {
+ return "package=" + mPkg + " uid=" + mUid + " windowToken=" + mWindowToken;
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 934f8a7..0e1641a 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1897,11 +1897,10 @@
return true;
}
- if (android.permission.flags.Flags.sensitiveNotificationAppProtection()) {
- if (mWmService.mSensitiveContentPackages
- .shouldBlockScreenCaptureForApp(getOwningPackage(), getOwningUid())) {
- return true;
- }
+ // block screen capture to protect sensitive notifications or content on the screen.
+ if (mWmService.mSensitiveContentPackages.shouldBlockScreenCaptureForApp(
+ getOwningPackage(), getOwningUid(), getWindowToken())) {
+ return true;
}
return !DevicePolicyCache.getInstance().isScreenCaptureAllowed(mShowUserId);
diff --git a/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceContentTest.java b/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceContentTest.java
new file mode 100644
index 0000000..dad36e7
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceContentTest.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static android.view.flags.Flags.FLAG_SENSITIVE_CONTENT_APP_PROTECTION;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import android.media.projection.MediaProjectionInfo;
+import android.media.projection.MediaProjectionManager;
+import android.os.Binder;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.provider.Settings;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableContext;
+import android.util.ArraySet;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.wm.SensitiveContentPackages.PackageInfo;
+import com.android.server.wm.WindowManagerInternal;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RequiresFlagsEnabled(FLAG_SENSITIVE_CONTENT_APP_PROTECTION)
+/**
+ * Test {@link SensitiveContentProtectionManagerService} for sensitive on screen content
+ * protection, the service protects sensitive content during screen share.
+ */
+public class SensitiveContentProtectionManagerServiceContentTest {
+ private final PackageInfo mPackageInfo =
+ new PackageInfo("test.package", 12345, new Binder());
+ private SensitiveContentProtectionManagerService mSensitiveContentProtectionManagerService;
+ private MediaProjectionManager.Callback mMediaPorjectionCallback;
+
+ @Mock private WindowManagerInternal mWindowManager;
+ @Mock private MediaProjectionManager mProjectionManager;
+ @Mock private MediaProjectionInfo mMediaProjectionInfo;
+
+ @Captor
+ private ArgumentCaptor<MediaProjectionManager.Callback> mMediaProjectionCallbackCaptor;
+ @Captor
+ private ArgumentCaptor<ArraySet<PackageInfo>> mPackageInfoCaptor;
+
+ @Rule
+ public final TestableContext mContext =
+ new TestableContext(getInstrumentation().getTargetContext(), null);
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mSensitiveContentProtectionManagerService =
+ new SensitiveContentProtectionManagerService(mContext);
+ mSensitiveContentProtectionManagerService.init(mProjectionManager, mWindowManager);
+ verify(mProjectionManager).addCallback(mMediaProjectionCallbackCaptor.capture(), any());
+ mMediaPorjectionCallback = mMediaProjectionCallbackCaptor.getValue();
+ }
+
+ @Test
+ public void testBlockAppWindowForScreenCapture() {
+ mMediaPorjectionCallback.onStart(mMediaProjectionInfo);
+ mSensitiveContentProtectionManagerService.setSensitiveContentProtection(
+ mPackageInfo.getWindowToken(), mPackageInfo.getPkg(), mPackageInfo.getUid(), true);
+ verify(mWindowManager, atLeast(1))
+ .addBlockScreenCaptureForApps(mPackageInfoCaptor.capture());
+ assertThat(mPackageInfoCaptor.getValue()).containsExactly(mPackageInfo);
+ }
+
+ @Test
+ public void testUnblockAppWindowForScreenCapture() {
+ mMediaPorjectionCallback.onStart(mMediaProjectionInfo);
+ mSensitiveContentProtectionManagerService.setSensitiveContentProtection(
+ mPackageInfo.getWindowToken(), mPackageInfo.getPkg(), mPackageInfo.getUid(), false);
+ verify(mWindowManager).removeBlockScreenCaptureForApps(mPackageInfoCaptor.capture());
+ assertThat(mPackageInfoCaptor.getValue()).containsExactly(mPackageInfo);
+ }
+
+ @Test
+ public void testAppWindowIsUnblockedBeforeScreenCapture() {
+ // when screen sharing is not active, no app window should be blocked.
+ mSensitiveContentProtectionManagerService.setSensitiveContentProtection(
+ mPackageInfo.getWindowToken(), mPackageInfo.getPkg(), mPackageInfo.getUid(), true);
+ verifyZeroInteractions(mWindowManager);
+ }
+
+ @Test
+ public void testAppWindowsAreUnblockedOnScreenCaptureEnd() {
+ mMediaPorjectionCallback.onStart(mMediaProjectionInfo);
+ mSensitiveContentProtectionManagerService.setSensitiveContentProtection(
+ mPackageInfo.getWindowToken(), mPackageInfo.getPkg(), mPackageInfo.getUid(), true);
+ // when screen sharing ends, all blocked app windows should be cleared.
+ mMediaPorjectionCallback.onStop(mMediaProjectionInfo);
+ verify(mWindowManager).clearBlockedApps();
+ }
+
+ @Test
+ public void testDeveloperOptionDisableFeature() {
+ mockDisabledViaDeveloperOption();
+ mMediaProjectionCallbackCaptor.getValue().onStart(mMediaProjectionInfo);
+ mSensitiveContentProtectionManagerService.setSensitiveContentProtection(
+ mPackageInfo.getWindowToken(), mPackageInfo.getPkg(), mPackageInfo.getUid(), true);
+ verifyZeroInteractions(mWindowManager);
+ }
+
+ private void mockDisabledViaDeveloperOption() {
+ Settings.Global.putInt(
+ mContext.getContentResolver(),
+ Settings.Global.DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS,
+ 1);
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceNotificationTest.java
similarity index 98%
rename from services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceTest.java
rename to services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceNotificationTest.java
index c298d51..08050a9 100644
--- a/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceNotificationTest.java
@@ -16,10 +16,10 @@
package com.android.server;
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
import static android.permission.flags.Flags.FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doCallRealMethod;
@@ -67,7 +67,11 @@
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@RequiresFlagsEnabled(FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION)
-public class SensitiveContentProtectionManagerServiceTest {
+/**
+ * Test {@link SensitiveContentProtectionManagerService} for sensitive notification protection,
+ * the service protects sensitive content during screen share.
+ */
+public class SensitiveContentProtectionManagerServiceNotificationTest {
private static final String NOTIFICATION_KEY_1 = "com.android.server.notification.TEST_KEY_1";
private static final String NOTIFICATION_KEY_2 = "com.android.server.notification.TEST_KEY_2";
diff --git a/services/tests/wmtests/src/com/android/server/wm/SensitiveContentPackagesTest.java b/services/tests/wmtests/src/com/android/server/wm/SensitiveContentPackagesTest.java
index 2986372..824b8d7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SensitiveContentPackagesTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SensitiveContentPackagesTest.java
@@ -16,10 +16,18 @@
package com.android.server.wm;
+import static android.permission.flags.Flags.FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION;
+import static android.view.flags.Flags.FLAG_SENSITIVE_CONTENT_APP_PROTECTION;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import android.os.Binder;
+import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.util.ArraySet;
import androidx.test.filters.SmallTest;
@@ -27,6 +35,7 @@
import com.android.server.wm.SensitiveContentPackages.PackageInfo;
import org.junit.After;
+import org.junit.Rule;
import org.junit.Test;
import java.util.Set;
@@ -44,23 +53,30 @@
private static final int APP_UID_1 = 5;
private static final int APP_UID_2 = 6;
- private static final int APP_UID_3 = 7;
+ private static final IBinder WINDOW_TOKEN_1 = new Binder();
+ private static final IBinder WINDOW_TOKEN_2 = new Binder();
private final SensitiveContentPackages mSensitiveContentPackages =
new SensitiveContentPackages();
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
@After
public void tearDown() {
mSensitiveContentPackages.clearBlockedApps();
}
+ private boolean shouldBlockScreenCaptureForApp(String pkg, int uid, IBinder windowToken) {
+ return mSensitiveContentPackages.shouldBlockScreenCaptureForApp(pkg, uid, windowToken);
+ }
+
@Test
- public void addBlockScreenCaptureForApps() {
+ @RequiresFlagsEnabled(FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION)
+ public void shouldBlockScreenCaptureForNotificationApps() {
ArraySet<PackageInfo> blockedApps = new ArraySet(
Set.of(new PackageInfo(APP_PKG_1, APP_UID_1),
- new PackageInfo(APP_PKG_1, APP_UID_2),
- new PackageInfo(APP_PKG_2, APP_UID_1),
new PackageInfo(APP_PKG_2, APP_UID_2)
));
@@ -68,17 +84,50 @@
assertTrue(modified);
- assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1));
- assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_3));
+ assertTrue(shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1, WINDOW_TOKEN_1));
+ assertTrue(shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1, WINDOW_TOKEN_2));
+ assertFalse(shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_2, WINDOW_TOKEN_2));
+ assertTrue(shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_2, WINDOW_TOKEN_1));
+ assertTrue(shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_2, WINDOW_TOKEN_2));
+ assertFalse(shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_1, WINDOW_TOKEN_2));
+ }
- assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_1));
- assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_3));
+ @Test
+ @RequiresFlagsEnabled(FLAG_SENSITIVE_CONTENT_APP_PROTECTION)
+ public void shouldBlockScreenCaptureForSensitiveContentOnScreenApps() {
+ ArraySet<PackageInfo> blockedApps = new ArraySet(
+ Set.of(new PackageInfo(APP_PKG_1, APP_UID_1, WINDOW_TOKEN_1),
+ new PackageInfo(APP_PKG_2, APP_UID_2, WINDOW_TOKEN_2)
+ ));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_1));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_3));
+ boolean modified = mSensitiveContentPackages.addBlockScreenCaptureForApps(blockedApps);
+ assertTrue(modified);
+
+ assertTrue(shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1, WINDOW_TOKEN_1));
+ assertFalse(shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1, WINDOW_TOKEN_2));
+
+ assertTrue(shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_2, WINDOW_TOKEN_2));
+ assertFalse(shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_2, WINDOW_TOKEN_1));
+ }
+
+ @Test
+ @RequiresFlagsEnabled(
+ {FLAG_SENSITIVE_CONTENT_APP_PROTECTION, FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION})
+ public void shouldBlockScreenCaptureForApps() {
+ ArraySet<PackageInfo> blockedApps = new ArraySet(
+ Set.of(new PackageInfo(APP_PKG_1, APP_UID_1, WINDOW_TOKEN_1),
+ new PackageInfo(APP_PKG_1, APP_UID_1),
+ new PackageInfo(APP_PKG_2, APP_UID_2, WINDOW_TOKEN_2)
+ ));
+
+ boolean modified = mSensitiveContentPackages.addBlockScreenCaptureForApps(blockedApps);
+ assertTrue(modified);
+
+ assertTrue(shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1, WINDOW_TOKEN_1));
+ assertTrue(shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1, WINDOW_TOKEN_2));
+
+ assertTrue(shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_2, WINDOW_TOKEN_2));
+ assertFalse(shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_2, WINDOW_TOKEN_1));
}
@Test
@@ -92,20 +141,8 @@
mSensitiveContentPackages.addBlockScreenCaptureForApps(blockedApps);
boolean modified = mSensitiveContentPackages.addBlockScreenCaptureForApps(blockedApps);
-
assertFalse(modified);
-
- assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1));
- assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_3));
-
- assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_1));
- assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_3));
-
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_1));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_3));
+ assertTrue(mSensitiveContentPackages.size() == 4);
}
@Test
@@ -121,65 +158,28 @@
boolean modified = mSensitiveContentPackages
.addBlockScreenCaptureForApps(
new ArraySet(Set.of(new PackageInfo(APP_PKG_3, APP_UID_1))));
-
assertTrue(modified);
-
- assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1));
- assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_3));
-
- assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_1));
- assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_3));
-
- assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_1));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_3));
+ assertTrue(mSensitiveContentPackages.size() == 5);
}
@Test
public void clearBlockedApps() {
ArraySet<PackageInfo> blockedApps = new ArraySet(
Set.of(new PackageInfo(APP_PKG_1, APP_UID_1),
- new PackageInfo(APP_PKG_1, APP_UID_2),
- new PackageInfo(APP_PKG_2, APP_UID_1),
- new PackageInfo(APP_PKG_2, APP_UID_2)
+ new PackageInfo(APP_PKG_2, APP_UID_2, WINDOW_TOKEN_2)
));
mSensitiveContentPackages.addBlockScreenCaptureForApps(blockedApps);
boolean modified = mSensitiveContentPackages.clearBlockedApps();
assertTrue(modified);
-
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_3));
-
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_1));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_3));
-
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_1));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_3));
+ assertTrue(mSensitiveContentPackages.size() == 0);
}
@Test
public void clearBlockedApps_alreadyEmpty() {
boolean modified = mSensitiveContentPackages.clearBlockedApps();
-
assertFalse(modified);
-
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_3));
-
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_1));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_3));
-
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_1));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_3));
+ assertTrue(mSensitiveContentPackages.size() == 0);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index c972e51..a0e64bf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -21,9 +21,11 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.permission.flags.Flags.FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.FLAG_OWN_FOCUS;
import static android.view.Display.INVALID_DISPLAY;
+import static android.view.flags.Flags.FLAG_SENSITIVE_CONTENT_APP_PROTECTION;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_SPY;
@@ -80,6 +82,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
import android.util.ArraySet;
import android.util.MergedConfiguration;
import android.view.ContentRecordingSession;
@@ -840,7 +843,8 @@
}
@Test
- public void addBlockScreenCaptureForApps() {
+ @RequiresFlagsEnabled(FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION)
+ public void shouldBlockScreenCaptureForNotificationApps() {
String testPackage = "test";
int ownerId1 = 20;
int ownerId2 = 21;
@@ -850,12 +854,59 @@
WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
wmInternal.addBlockScreenCaptureForApps(blockedPackages);
+ verify(mWm).refreshScreenCaptureDisabled();
+
+ // window client token parameter is ignored for this feature.
+ assertTrue(mWm.mSensitiveContentPackages
+ .shouldBlockScreenCaptureForApp(testPackage, ownerId1, new Binder()));
+ assertFalse(mWm.mSensitiveContentPackages
+ .shouldBlockScreenCaptureForApp(testPackage, ownerId2, new Binder()));
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_SENSITIVE_CONTENT_APP_PROTECTION)
+ public void shouldBlockScreenCaptureForSensitiveContentOnScreenApps() {
+ String testPackage = "test";
+ int ownerId1 = 20;
+ final IBinder windowClientToken = new Binder("window client token");
+ PackageInfo blockedPackage = new PackageInfo(testPackage, ownerId1, windowClientToken);
+ ArraySet<PackageInfo> blockedPackages = new ArraySet();
+ blockedPackages.add(blockedPackage);
+
+ WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
+ wmInternal.addBlockScreenCaptureForApps(blockedPackages);
+ verify(mWm).refreshScreenCaptureDisabled();
assertTrue(mWm.mSensitiveContentPackages
- .shouldBlockScreenCaptureForApp(testPackage, ownerId1));
+ .shouldBlockScreenCaptureForApp(testPackage, ownerId1, windowClientToken));
assertFalse(mWm.mSensitiveContentPackages
- .shouldBlockScreenCaptureForApp(testPackage, ownerId2));
+ .shouldBlockScreenCaptureForApp(testPackage, ownerId1, new Binder()));
+ }
+
+ @Test
+ @RequiresFlagsEnabled(
+ {FLAG_SENSITIVE_CONTENT_APP_PROTECTION, FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION})
+ public void shouldBlockScreenCaptureForApps() {
+ String testPackage = "test";
+ int ownerId1 = 20;
+ int ownerId2 = 21;
+ final IBinder windowClientToken = new Binder("window client token");
+ PackageInfo blockedPackage1 = new PackageInfo(testPackage, ownerId1);
+ PackageInfo blockedPackage2 = new PackageInfo(testPackage, ownerId1, windowClientToken);
+ ArraySet<PackageInfo> blockedPackages = new ArraySet();
+ blockedPackages.add(blockedPackage1);
+ blockedPackages.add(blockedPackage2);
+
+ WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
+ wmInternal.addBlockScreenCaptureForApps(blockedPackages);
verify(mWm).refreshScreenCaptureDisabled();
+
+ assertTrue(mWm.mSensitiveContentPackages
+ .shouldBlockScreenCaptureForApp(testPackage, ownerId1, windowClientToken));
+ assertTrue(mWm.mSensitiveContentPackages
+ .shouldBlockScreenCaptureForApp(testPackage, ownerId1, new Binder()));
+ assertFalse(mWm.mSensitiveContentPackages
+ .shouldBlockScreenCaptureForApp(testPackage, ownerId2, new Binder()));
}
@Test
@@ -891,10 +942,6 @@
wmInternal.addBlockScreenCaptureForApps(blockedPackages);
wmInternal.addBlockScreenCaptureForApps(blockedPackages2);
- assertTrue(mWm.mSensitiveContentPackages
- .shouldBlockScreenCaptureForApp(testPackage, ownerId1));
- assertTrue(mWm.mSensitiveContentPackages
- .shouldBlockScreenCaptureForApp(testPackage, ownerId2));
verify(mWm, times(2)).refreshScreenCaptureDisabled();
}
@@ -909,9 +956,6 @@
WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
wmInternal.addBlockScreenCaptureForApps(blockedPackages);
wmInternal.clearBlockedApps();
-
- assertFalse(mWm.mSensitiveContentPackages
- .shouldBlockScreenCaptureForApp(testPackage, ownerId1));
verify(mWm, times(2)).refreshScreenCaptureDisabled();
}