summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Richard MacGregor <rmacgregor@google.com> 2024-03-20 21:07:09 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2024-03-20 21:07:09 +0000
commit3bb6b83acf4cf6b7542cd70ccec04d246f3f13dd (patch)
tree45019465eaf63ef611ebc8938175f2c0ded55633
parent6739c33371c29a3bdfc58f3b1826f9f1c0a2cc15 (diff)
parentdbb5254f8bc2fdd32278e9c10310898b24983a42 (diff)
Merge "Update projection exemption calculation" into main
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerImpl.java64
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerFlagDisabledTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerTest.kt79
3 files changed, 125 insertions, 21 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerImpl.java
index 18ec68bd89eb..1f4c3cd9a017 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerImpl.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.policy;
+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.server.notification.Flags.screenshareNotificationHiding;
@@ -23,6 +24,7 @@ import static com.android.server.notification.Flags.screenshareNotificationHidin
import android.annotation.MainThread;
import android.app.IActivityManager;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.database.ExecutorContentObserver;
import android.media.projection.MediaProjectionInfo;
import android.media.projection.MediaProjectionManager;
@@ -33,6 +35,9 @@ import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
import android.util.Log;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
@@ -52,6 +57,7 @@ public class SensitiveNotificationProtectionControllerImpl
implements SensitiveNotificationProtectionController {
private static final String LOG_TAG = "SNPC";
private final SensitiveNotificationProtectionControllerLogger mLogger;
+ private final PackageManager mPackageManager;
private final ArraySet<String> mExemptPackages = new ArraySet<>();
private final ListenerSet<Runnable> mListeners = new ListenerSet<>();
private volatile MediaProjectionInfo mProjection;
@@ -64,17 +70,7 @@ public class SensitiveNotificationProtectionControllerImpl
public void onStart(MediaProjectionInfo info) {
Trace.beginSection("SNPC.onProjectionStart");
try {
- if (mDisableScreenShareProtections) {
- Log.w(LOG_TAG,
- "Screen share protections disabled, ignoring projectionstart");
- mLogger.logProjectionStart(false, info.getPackageName());
- return;
- }
-
- // Only enable sensitive content protection if sharing full screen
- // Launch cookie only set (non-null) if sharing single app/task
- updateProjectionStateAndNotifyListeners(
- (info.getLaunchCookie() == null) ? info : null);
+ updateProjectionStateAndNotifyListeners(info);
mLogger.logProjectionStart(isSensitiveStateActive(), info.getPackageName());
} finally {
Trace.endSection();
@@ -99,10 +95,12 @@ public class SensitiveNotificationProtectionControllerImpl
GlobalSettings settings,
MediaProjectionManager mediaProjectionManager,
IActivityManager activityManager,
+ PackageManager packageManager,
@Main Handler mainHandler,
@Background Executor bgExecutor,
SensitiveNotificationProtectionControllerLogger logger) {
mLogger = logger;
+ mPackageManager = packageManager;
if (!screenshareNotificationHiding()) {
return;
@@ -168,7 +166,7 @@ public class SensitiveNotificationProtectionControllerImpl
mExemptPackages.addAll(exemptPackages);
if (mProjection != null) {
- mListeners.forEach(Runnable::run);
+ updateProjectionStateAndNotifyListeners(mProjection);
}
}
@@ -177,13 +175,13 @@ public class SensitiveNotificationProtectionControllerImpl
* listeners
*/
@MainThread
- private void updateProjectionStateAndNotifyListeners(MediaProjectionInfo info) {
+ private void updateProjectionStateAndNotifyListeners(@Nullable MediaProjectionInfo info) {
Assert.isMainThread();
// capture previous state
boolean wasSensitive = isSensitiveStateActive();
// update internal state
- mProjection = info;
+ mProjection = getNonExemptProjectionInfo(info);
// if either previous or new state is sensitive, notify listeners.
if (wasSensitive || isSensitiveStateActive()) {
@@ -191,6 +189,36 @@ public class SensitiveNotificationProtectionControllerImpl
}
}
+ private MediaProjectionInfo getNonExemptProjectionInfo(@Nullable MediaProjectionInfo info) {
+ if (mDisableScreenShareProtections) {
+ Log.w(LOG_TAG, "Screen share protections disabled");
+ return null;
+ } else if (info != null && mExemptPackages.contains(info.getPackageName())) {
+ Log.w(LOG_TAG, "Screen share protections exempt for package " + info.getPackageName());
+ return null;
+ } else if (info != null && canRecordSensitiveContent(info.getPackageName())) {
+ Log.w(LOG_TAG, "Screen share protections exempt for package " + info.getPackageName()
+ + " via permission");
+ return null;
+ } else if (info != null && info.getLaunchCookie() != null) {
+ // Only enable sensitive content protection if sharing full screen
+ // Launch cookie only set (non-null) if sharing single app/task
+ Log.w(LOG_TAG, "Screen share protections exempt for single app screenshare");
+ return null;
+ }
+ return info;
+ }
+
+ private boolean canRecordSensitiveContent(@NonNull String packageName) {
+ // RECORD_SENSITIVE_CONTENT is flagged api on sensitiveNotificationAppProtection
+ if (sensitiveNotificationAppProtection()) {
+ return mPackageManager.checkPermission(
+ android.Manifest.permission.RECORD_SENSITIVE_CONTENT, packageName)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+ return false;
+ }
+
@Override
public void registerSensitiveStateListener(Runnable onSensitiveStateChanged) {
mListeners.addIfAbsent(onSensitiveStateChanged);
@@ -201,15 +229,9 @@ public class SensitiveNotificationProtectionControllerImpl
mListeners.remove(onSensitiveStateChanged);
}
- // TODO(b/323396693): opportunity for optimization
@Override
public boolean isSensitiveStateActive() {
- MediaProjectionInfo projection = mProjection;
- if (projection == null) {
- return false;
- }
-
- return !mExemptPackages.contains(projection.getPackageName());
+ return mProjection != null;
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerFlagDisabledTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerFlagDisabledTest.kt
index 933b5b519672..358709f48ea8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerFlagDisabledTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerFlagDisabledTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.policy
import android.app.IActivityManager
+import android.content.pm.PackageManager
import android.media.projection.MediaProjectionManager
import android.os.Handler
import android.platform.test.annotations.DisableFlags
@@ -44,6 +45,7 @@ class SensitiveNotificationProtectionControllerFlagDisabledTest : SysuiTestCase(
@Mock private lateinit var handler: Handler
@Mock private lateinit var activityManager: IActivityManager
@Mock private lateinit var mediaProjectionManager: MediaProjectionManager
+ @Mock private lateinit var packageManager: PackageManager
private lateinit var controller: SensitiveNotificationProtectionControllerImpl
@Before
@@ -56,6 +58,7 @@ class SensitiveNotificationProtectionControllerFlagDisabledTest : SysuiTestCase(
FakeGlobalSettings(),
mediaProjectionManager,
activityManager,
+ packageManager,
handler,
FakeExecutor(FakeSystemClock()),
logger
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerTest.kt
index 4b4e315f5533..7dfe6d01912f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerTest.kt
@@ -25,9 +25,14 @@ import android.app.Notification.VISIBILITY_PUBLIC
import android.app.NotificationChannel
import android.app.NotificationManager.IMPORTANCE_HIGH
import android.app.NotificationManager.VISIBILITY_NO_OVERRIDE
+import android.content.pm.PackageManager
import android.media.projection.MediaProjectionInfo
import android.media.projection.MediaProjectionManager
+import android.permission.flags.Flags.FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION
import android.platform.test.annotations.EnableFlags
+import android.platform.test.annotations.RequiresFlagsDisabled
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.provider.Settings.Global.DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
@@ -48,9 +53,11 @@ import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyString
import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.Mockito.times
@@ -64,10 +71,13 @@ import org.mockito.MockitoAnnotations
@RunWithLooper
@EnableFlags(Flags.FLAG_SCREENSHARE_NOTIFICATION_HIDING)
class SensitiveNotificationProtectionControllerTest : SysuiTestCase() {
+ @get:Rule val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
private val logger = SensitiveNotificationProtectionControllerLogger(logcatLogBuffer())
@Mock private lateinit var activityManager: IActivityManager
@Mock private lateinit var mediaProjectionManager: MediaProjectionManager
+ @Mock private lateinit var packageManager: PackageManager
@Mock private lateinit var mediaProjectionInfo: MediaProjectionInfo
@Mock private lateinit var listener1: Runnable
@Mock private lateinit var listener2: Runnable
@@ -87,6 +97,9 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() {
whenever(activityManager.bugreportWhitelistedPackages)
.thenReturn(listOf(BUGREPORT_PACKAGE_NAME))
+ whenever(packageManager.checkPermission(anyString(), anyString()))
+ .thenReturn(PackageManager.PERMISSION_DENIED)
+
executor = FakeExecutor(FakeSystemClock())
globalSettings = FakeGlobalSettings()
controller =
@@ -95,6 +108,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() {
globalSettings,
mediaProjectionManager,
activityManager,
+ packageManager,
mockExecutorHandler(executor),
executor,
logger
@@ -237,6 +251,36 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() {
}
@Test
+ @RequiresFlagsDisabled(FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION)
+ fun isSensitiveStateActive_projectionActive_permissionExempt_flagDisabled_true() {
+ whenever(
+ packageManager.checkPermission(
+ android.Manifest.permission.RECORD_SENSITIVE_CONTENT,
+ mediaProjectionInfo.packageName
+ )
+ )
+ .thenReturn(PackageManager.PERMISSION_GRANTED)
+ mediaProjectionCallback.onStart(mediaProjectionInfo)
+
+ assertTrue(controller.isSensitiveStateActive)
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION)
+ fun isSensitiveStateActive_projectionActive_permissionExempt_false() {
+ whenever(
+ packageManager.checkPermission(
+ android.Manifest.permission.RECORD_SENSITIVE_CONTENT,
+ mediaProjectionInfo.packageName
+ )
+ )
+ .thenReturn(PackageManager.PERMISSION_GRANTED)
+ mediaProjectionCallback.onStart(mediaProjectionInfo)
+
+ assertFalse(controller.isSensitiveStateActive)
+ }
+
+ @Test
fun isSensitiveStateActive_projectionActive_bugReportHandlerExempt_false() {
whenever(mediaProjectionInfo.packageName).thenReturn(BUGREPORT_PACKAGE_NAME)
mediaProjectionCallback.onStart(mediaProjectionInfo)
@@ -309,6 +353,40 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() {
}
@Test
+ @RequiresFlagsDisabled(FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION)
+ fun shouldProtectNotification_projectionActive_permissionExempt_flagDisabled_true() {
+ whenever(
+ packageManager.checkPermission(
+ android.Manifest.permission.RECORD_SENSITIVE_CONTENT,
+ mediaProjectionInfo.packageName
+ )
+ )
+ .thenReturn(PackageManager.PERMISSION_GRANTED)
+ mediaProjectionCallback.onStart(mediaProjectionInfo)
+
+ val notificationEntry = setupNotificationEntry(TEST_PACKAGE_NAME, false)
+
+ assertTrue(controller.shouldProtectNotification(notificationEntry))
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION)
+ fun shouldProtectNotification_projectionActive_permissionExempt_false() {
+ whenever(
+ packageManager.checkPermission(
+ android.Manifest.permission.RECORD_SENSITIVE_CONTENT,
+ mediaProjectionInfo.packageName
+ )
+ )
+ .thenReturn(PackageManager.PERMISSION_GRANTED)
+ mediaProjectionCallback.onStart(mediaProjectionInfo)
+
+ val notificationEntry = setupNotificationEntry(TEST_PACKAGE_NAME, false)
+
+ assertFalse(controller.shouldProtectNotification(notificationEntry))
+ }
+
+ @Test
fun shouldProtectNotification_projectionActive_bugReportHandlerExempt_false() {
whenever(mediaProjectionInfo.packageName).thenReturn(BUGREPORT_PACKAGE_NAME)
mediaProjectionCallback.onStart(mediaProjectionInfo)
@@ -327,6 +405,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() {
assertFalse(controller.shouldProtectNotification(notificationEntry))
}
+
@Test
fun shouldProtectNotification_projectionActive_publicNotification_false() {
mediaProjectionCallback.onStart(mediaProjectionInfo)