diff options
| author | 2024-03-20 21:07:09 +0000 | |
|---|---|---|
| committer | 2024-03-20 21:07:09 +0000 | |
| commit | 3bb6b83acf4cf6b7542cd70ccec04d246f3f13dd (patch) | |
| tree | 45019465eaf63ef611ebc8938175f2c0ded55633 | |
| parent | 6739c33371c29a3bdfc58f3b1826f9f1c0a2cc15 (diff) | |
| parent | dbb5254f8bc2fdd32278e9c10310898b24983a42 (diff) | |
Merge "Update projection exemption calculation" into main
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) |