Loading packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerFlagDisabledTest.kt +4 −1 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.policy import android.app.IActivityManager import android.app.role.RoleManager import android.content.pm.PackageManager import android.media.projection.MediaProjectionManager import android.os.Handler Loading Loading @@ -48,6 +49,7 @@ class SensitiveNotificationProtectionControllerFlagDisabledTest : SysuiTestCase( @Mock private lateinit var mediaProjectionManager: MediaProjectionManager @Mock private lateinit var packageManager: PackageManager @Mock private lateinit var telephonyManager: TelephonyManager @Mock private lateinit var roleManager: RoleManager private lateinit var controller: SensitiveNotificationProtectionControllerImpl @Before Loading @@ -62,9 +64,10 @@ class SensitiveNotificationProtectionControllerFlagDisabledTest : SysuiTestCase( activityManager, packageManager, telephonyManager, roleManager, handler, FakeExecutor(FakeSystemClock()), logger logger, ) } Loading packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerImpl.java +64 −1 Original line number Diff line number Diff line Loading @@ -24,6 +24,9 @@ import static com.android.systemui.Flags.screenshareNotificationHidingBugFix; import android.annotation.MainThread; import android.app.IActivityManager; import android.app.role.OnRoleHoldersChangedListener; import android.app.role.RoleManager; import android.companion.AssociationRequest; import android.content.Context; import android.content.pm.PackageManager; import android.database.ExecutorContentObserver; Loading Loading @@ -51,6 +54,8 @@ import com.android.systemui.util.Assert; import com.android.systemui.util.ListenerSet; import com.android.systemui.util.settings.GlobalSettings; import java.util.List; import java.util.Objects; import java.util.Random; import java.util.concurrent.Executor; Loading @@ -63,12 +68,14 @@ public class SensitiveNotificationProtectionControllerImpl private static final String LOG_TAG = "SNPC"; private final SensitiveNotificationProtectionControllerLogger mLogger; private final PackageManager mPackageManager; private final RoleManager mRoleManager; // Packages exempt from projection session protections (if they start a projection session) private final ArraySet<String> mSessionProtectionExemptPackages = new ArraySet<>(); // Packages exempt from individual notification protections (if they post a notification) private final ArraySet<String> mNotificationProtectionExemptPackages = new ArraySet<>(); private final ListenerSet<Runnable> mListeners = new ListenerSet<>(); private volatile MediaProjectionInfo mProjection; private ArraySet<RoleHolder> mNotificationProtectionExemptByRolePackages = new ArraySet<>(); private SensitiveNotificatioMediaProjectionSession mActiveMediaProjectionSession; boolean mDisableScreenShareProtections = false; Loading Loading @@ -128,6 +135,27 @@ public class SensitiveNotificationProtectionControllerImpl } }; @VisibleForTesting final OnRoleHoldersChangedListener mRoleHoldersChangedListener = new OnRoleHoldersChangedListener() { @Override public void onRoleHoldersChanged(@NonNull String roleName, @NonNull UserHandle user) { if (!roleName.equals(AssociationRequest.DEVICE_PROFILE_APP_STREAMING)) { return; } List<String> appStreamingRoleHolders = mRoleManager.getRoleHoldersAsUser( roleName, user); ArraySet<RoleHolder> roleHolders = new ArraySet<>(); for (String appStreamingRoleHolder : appStreamingRoleHolders) { RoleHolder roleHolder = new RoleHolder(appStreamingRoleHolder, user); roleHolders.add(roleHolder); } mNotificationProtectionExemptByRolePackages = roleHolders; } }; private void logSensitiveContentProtectionSessionStart( long sessionId, int projectionAppUid, boolean exempt) { mActiveMediaProjectionSession = Loading Loading @@ -166,11 +194,13 @@ public class SensitiveNotificationProtectionControllerImpl IActivityManager activityManager, PackageManager packageManager, TelephonyManager telephonyManager, RoleManager roleManager, @Main Handler mainHandler, @Background Executor bgExecutor, SensitiveNotificationProtectionControllerLogger logger) { mLogger = logger; mPackageManager = packageManager; mRoleManager = roleManager; if (!screenshareNotificationHiding()) { return; Loading Loading @@ -215,6 +245,8 @@ public class SensitiveNotificationProtectionControllerImpl }); mediaProjectionManager.addCallback(mMediaProjectionCallback, mainHandler); roleManager.addOnRoleHoldersChangedListenerAsUser(bgExecutor, mRoleHoldersChangedListener, UserHandle.ALL); } @NonNull Loading Loading @@ -314,6 +346,10 @@ public class SensitiveNotificationProtectionControllerImpl Log.w(LOG_TAG, "Screen share protections exempt for package " + info.getPackageName() + " via permission"); return null; } else if (info != null && isAppStreamingRoleHolder(info)) { Log.w(LOG_TAG, "Screen share protections exempt for package " + info.getPackageName() + " via role(s) held"); 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 Loading @@ -323,6 +359,11 @@ public class SensitiveNotificationProtectionControllerImpl return info; } private boolean isAppStreamingRoleHolder(@NonNull MediaProjectionInfo info) { return mNotificationProtectionExemptByRolePackages.contains( new RoleHolder(info.getPackageName(), info.getUserHandle())); } private boolean canRecordSensitiveContent(@NonNull String packageName) { // RECORD_SENSITIVE_CONTENT is flagged api on sensitiveNotificationAppProtection if (sensitiveNotificationAppProtection()) { Loading Loading @@ -382,4 +423,26 @@ public class SensitiveNotificationProtectionControllerImpl boolean userForcesRedaction = entry.isChannelVisibilityPrivate(); return notificationRequestsRedaction || userForcesRedaction; } private static final class RoleHolder { private final String mPackageName; private final UserHandle mUserHandle; RoleHolder(String packageName, UserHandle userHandle) { mPackageName = packageName; mUserHandle = userHandle; } @Override public boolean equals(Object o) { if (!(o instanceof RoleHolder that)) return false; return Objects.equals(mPackageName, that.mPackageName) && Objects.equals( mUserHandle, that.mUserHandle); } @Override public int hashCode() { return Objects.hash(mPackageName, mUserHandle); } } } packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerTest.kt +47 −19 Original line number Diff line number Diff line Loading @@ -25,6 +25,9 @@ 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.app.role.OnRoleHoldersChangedListener import android.app.role.RoleManager import android.companion.AssociationRequest import android.content.pm.PackageManager import android.media.projection.MediaProjectionInfo import android.media.projection.MediaProjectionManager Loading Loading @@ -89,6 +92,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { @Mock private lateinit var activityManager: IActivityManager @Mock private lateinit var mediaProjectionManager: MediaProjectionManager @Mock private lateinit var packageManager: PackageManager @Mock private lateinit var roleManager: RoleManager @Mock private lateinit var telephonyManager: TelephonyManager @Mock private lateinit var listener1: Runnable @Mock private lateinit var listener2: Runnable Loading @@ -98,6 +102,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { private lateinit var executor: FakeExecutor private lateinit var globalSettings: FakeGlobalSettings private lateinit var mediaProjectionCallback: MediaProjectionManager.Callback private lateinit var roleHolderCallback: OnRoleHoldersChangedListener private lateinit var controller: SensitiveNotificationProtectionControllerImpl private lateinit var mediaProjectionInfo: MediaProjectionInfo Loading @@ -117,14 +122,14 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { whenever( packageManager.getPackageUidAsUser( TEST_PROJECTION_PACKAGE_NAME, UserHandle.CURRENT.identifier UserHandle.CURRENT.identifier, ) ) .thenReturn(TEST_PROJECTION_PACKAGE_UID) whenever( packageManager.getPackageUidAsUser( BUGREPORT_PACKAGE_NAME, UserHandle.CURRENT.identifier UserHandle.CURRENT.identifier, ) ) .thenReturn(BUGREPORT_PACKAGE_UID) Loading @@ -134,7 +139,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { whenever( packageManager.getPackageUidAsUser( mContext.packageName, UserHandle.CURRENT.identifier UserHandle.CURRENT.identifier, ) ) .thenReturn(mContext.applicationInfo.uid) Loading @@ -155,9 +160,10 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { activityManager, packageManager, telephonyManager, roleManager, mockExecutorHandler(executor), executor, logger logger, ) // Process pending work (getting global setting and list of exemptions) Loading @@ -167,6 +173,9 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { mediaProjectionCallback = withArgCaptor { verify(mediaProjectionManager).addCallback(capture(), any()) } roleHolderCallback = withArgCaptor { verify(roleManager).addOnRoleHoldersChangedListenerAsUser(any(), capture(), any()) } } @After Loading Loading @@ -307,7 +316,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { whenever( packageManager.checkPermission( android.Manifest.permission.RECORD_SENSITIVE_CONTENT, mediaProjectionInfo.packageName mediaProjectionInfo.packageName, ) ) .thenReturn(PackageManager.PERMISSION_GRANTED) Loading @@ -322,7 +331,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { whenever( packageManager.checkPermission( android.Manifest.permission.RECORD_SENSITIVE_CONTENT, mediaProjectionInfo.packageName mediaProjectionInfo.packageName, ) ) .thenReturn(PackageManager.PERMISSION_GRANTED) Loading @@ -339,6 +348,25 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { assertFalse(controller.isSensitiveStateActive) } @Test fun isSensitiveStateActive_projectionActive_appStreamingRoleHolderExempt_false() { setShareFullScreen() whenever( roleManager.getRoleHoldersAsUser( AssociationRequest.DEVICE_PROFILE_APP_STREAMING, mediaProjectionInfo.userHandle, ) ) .thenReturn(listOf(TEST_PROJECTION_PACKAGE_NAME)) roleHolderCallback.onRoleHoldersChanged( AssociationRequest.DEVICE_PROFILE_APP_STREAMING, mediaProjectionInfo.userHandle, ) mediaProjectionCallback.onStart(mediaProjectionInfo) assertFalse(controller.isSensitiveStateActive) } @Test fun isSensitiveStateActive_projectionActive_disabledViaDevOption_false() { setDisabledViaDeveloperOption() Loading Loading @@ -449,7 +477,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { whenever( packageManager.checkPermission( android.Manifest.permission.RECORD_SENSITIVE_CONTENT, mediaProjectionInfo.packageName mediaProjectionInfo.packageName, ) ) .thenReturn(PackageManager.PERMISSION_GRANTED) Loading @@ -466,7 +494,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { whenever( packageManager.checkPermission( android.Manifest.permission.RECORD_SENSITIVE_CONTENT, mediaProjectionInfo.packageName mediaProjectionInfo.packageName, ) ) .thenReturn(PackageManager.PERMISSION_GRANTED) Loading Loading @@ -528,7 +556,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { eq(TEST_PROJECTION_PACKAGE_UID), eq(false), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__STATE__START), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI) eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI), ) } Loading @@ -541,7 +569,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { eq(TEST_PROJECTION_PACKAGE_UID), eq(false), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__STATE__STOP), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI) eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI), ) } } Loading @@ -559,7 +587,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { eq(TEST_PROJECTION_PACKAGE_UID), eq(true), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__STATE__START), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI) eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI), ) } Loading @@ -572,7 +600,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { eq(TEST_PROJECTION_PACKAGE_UID), eq(true), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__STATE__STOP), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI) eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI), ) } } Loading @@ -590,7 +618,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { eq(TEST_PROJECTION_PACKAGE_UID), eq(true), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__STATE__START), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI) eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI), ) } Loading @@ -603,7 +631,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { eq(TEST_PROJECTION_PACKAGE_UID), eq(true), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__STATE__STOP), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI) eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI), ) } } Loading @@ -623,7 +651,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { eq(mContext.applicationInfo.uid), eq(true), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__STATE__START), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI) eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI), ) } Loading @@ -636,7 +664,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { eq(mContext.applicationInfo.uid), eq(true), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__STATE__STOP), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI) eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI), ) } } Loading @@ -654,7 +682,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { eq(BUGREPORT_PACKAGE_UID), eq(true), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__STATE__START), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI) eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI), ) } Loading @@ -667,7 +695,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { eq(BUGREPORT_PACKAGE_UID), eq(true), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__STATE__STOP), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI) eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI), ) } } Loading Loading @@ -757,7 +785,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { return setupNotificationEntry( packageName, overrideVisibility = true, overrideChannelVisibility = true overrideChannelVisibility = true, ) } Loading services/core/java/com/android/server/SensitiveContentProtectionManagerService.java +18 −3 Original line number Diff line number Diff line Loading @@ -31,6 +31,8 @@ import static com.android.server.wm.WindowManagerInternal.OnWindowRemovedListene import android.annotation.NonNull; import android.annotation.Nullable; import android.app.role.RoleManager; import android.companion.AssociationRequest; import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManager; Loading Loading @@ -81,6 +83,8 @@ public final class SensitiveContentProtectionManagerService extends SystemServic private PackageManagerInternal mPackageManagerInternal; private RoleManager mRoleManager; @Nullable private WindowManagerInternal mWindowManager; Loading Loading @@ -225,7 +229,8 @@ public final class SensitiveContentProtectionManagerService extends SystemServic } @Override public void onStart() {} public void onStart() { } @Override public void onBootPhase(int phase) { Loading @@ -237,6 +242,7 @@ public final class SensitiveContentProtectionManagerService extends SystemServic init(getContext().getSystemService(MediaProjectionManager.class), LocalServices.getService(WindowManagerInternal.class), LocalServices.getService(PackageManagerInternal.class), getContext().getSystemService(RoleManager.class), getExemptedPackages() ); if (sensitiveContentAppProtection()) { Loading @@ -247,7 +253,8 @@ public final class SensitiveContentProtectionManagerService extends SystemServic @VisibleForTesting void init(MediaProjectionManager projectionManager, WindowManagerInternal windowManager, PackageManagerInternal packageManagerInternal, ArraySet<String> exemptedPackages) { PackageManagerInternal packageManagerInternal, RoleManager roleManager, ArraySet<String> exemptedPackages) { if (DEBUG) Log.d(TAG, "init"); Objects.requireNonNull(projectionManager); Loading @@ -256,6 +263,7 @@ public final class SensitiveContentProtectionManagerService extends SystemServic mProjectionManager = projectionManager; mWindowManager = windowManager; mPackageManagerInternal = packageManagerInternal; mRoleManager = roleManager; mExemptedPackages = exemptedPackages; // TODO(b/317250444): use MediaProjectionManagerService directly, reduces unnecessary Loading Loading @@ -312,6 +320,8 @@ public final class SensitiveContentProtectionManagerService extends SystemServic boolean isPackageExempted = (mExemptedPackages != null && mExemptedPackages.contains( projectionInfo.getPackageName())) || canRecordSensitiveContent(projectionInfo.getPackageName()) || holdsAppStreamingRole(projectionInfo.getPackageName(), projectionInfo.getUserHandle()) || isAutofillServiceRecorderPackage(projectionInfo.getUserHandle().getIdentifier(), projectionInfo.getPackageName()); // TODO(b/324447419): move GlobalSettings lookup to background thread Loading Loading @@ -348,6 +358,11 @@ public final class SensitiveContentProtectionManagerService extends SystemServic } } private boolean holdsAppStreamingRole(String packageName, UserHandle userHandle) { return mRoleManager.getRoleHoldersAsUser( AssociationRequest.DEVICE_PROFILE_APP_STREAMING, userHandle).contains(packageName); } private void onProjectionEnd() { synchronized (mSensitiveContentProtectionLock) { mProjectionActive = false; Loading services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceContentTest.java +21 −2 Original line number Diff line number Diff line Loading @@ -26,7 +26,10 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.role.RoleManager; import android.companion.AssociationRequest; import android.content.pm.PackageManagerInternal; import android.media.projection.MediaProjectionInfo; import android.media.projection.MediaProjectionManager; Loading Loading @@ -54,6 +57,7 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.List; import java.util.Set; @SmallTest Loading @@ -74,6 +78,7 @@ public class SensitiveContentProtectionManagerServiceContentTest { @Mock private WindowManagerInternal mWindowManager; @Mock private MediaProjectionManager mProjectionManager; @Mock private PackageManagerInternal mPackageManagerInternal; @Mock private RoleManager mRoleManager; private MediaProjectionInfo mMediaProjectionInfo; @Captor Loading @@ -93,7 +98,8 @@ public class SensitiveContentProtectionManagerServiceContentTest { mSensitiveContentProtectionManagerService = new SensitiveContentProtectionManagerService(mContext); mSensitiveContentProtectionManagerService.init(mProjectionManager, mWindowManager, mPackageManagerInternal, new ArraySet<>(Set.of(mExemptedScreenRecorderPackage))); mPackageManagerInternal, mRoleManager, new ArraySet<>(Set.of(mExemptedScreenRecorderPackage))); verify(mProjectionManager).addCallback(mMediaProjectionCallbackCaptor.capture(), any()); mMediaPorjectionCallback = mMediaProjectionCallbackCaptor.getValue(); mMediaProjectionInfo = Loading Loading @@ -169,6 +175,19 @@ public class SensitiveContentProtectionManagerServiceContentTest { verify(mWindowManager, never()).addBlockScreenCaptureForApps(mPackageInfoCaptor.capture()); } @Test public void testAppStreamingRoleHolderExemption() { when(mRoleManager.getRoleHoldersAsUser( AssociationRequest.DEVICE_PROFILE_APP_STREAMING, mMediaProjectionInfo.getUserHandle())).thenReturn( List.of(mMediaProjectionInfo.getPackageName())); mMediaPorjectionCallback.onStart(mMediaProjectionInfo); mSensitiveContentProtectionManagerService.setSensitiveContentProtection( mPackageInfo.getWindowToken(), mPackageInfo.getPkg(), mPackageInfo.getUid(), true); verify(mWindowManager, never()).addBlockScreenCaptureForApps(mPackageInfoCaptor.capture()); } private void mockDisabledViaDeveloperOption() { Settings.Global.putInt( mContext.getContentResolver(), Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerFlagDisabledTest.kt +4 −1 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.policy import android.app.IActivityManager import android.app.role.RoleManager import android.content.pm.PackageManager import android.media.projection.MediaProjectionManager import android.os.Handler Loading Loading @@ -48,6 +49,7 @@ class SensitiveNotificationProtectionControllerFlagDisabledTest : SysuiTestCase( @Mock private lateinit var mediaProjectionManager: MediaProjectionManager @Mock private lateinit var packageManager: PackageManager @Mock private lateinit var telephonyManager: TelephonyManager @Mock private lateinit var roleManager: RoleManager private lateinit var controller: SensitiveNotificationProtectionControllerImpl @Before Loading @@ -62,9 +64,10 @@ class SensitiveNotificationProtectionControllerFlagDisabledTest : SysuiTestCase( activityManager, packageManager, telephonyManager, roleManager, handler, FakeExecutor(FakeSystemClock()), logger logger, ) } Loading
packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerImpl.java +64 −1 Original line number Diff line number Diff line Loading @@ -24,6 +24,9 @@ import static com.android.systemui.Flags.screenshareNotificationHidingBugFix; import android.annotation.MainThread; import android.app.IActivityManager; import android.app.role.OnRoleHoldersChangedListener; import android.app.role.RoleManager; import android.companion.AssociationRequest; import android.content.Context; import android.content.pm.PackageManager; import android.database.ExecutorContentObserver; Loading Loading @@ -51,6 +54,8 @@ import com.android.systemui.util.Assert; import com.android.systemui.util.ListenerSet; import com.android.systemui.util.settings.GlobalSettings; import java.util.List; import java.util.Objects; import java.util.Random; import java.util.concurrent.Executor; Loading @@ -63,12 +68,14 @@ public class SensitiveNotificationProtectionControllerImpl private static final String LOG_TAG = "SNPC"; private final SensitiveNotificationProtectionControllerLogger mLogger; private final PackageManager mPackageManager; private final RoleManager mRoleManager; // Packages exempt from projection session protections (if they start a projection session) private final ArraySet<String> mSessionProtectionExemptPackages = new ArraySet<>(); // Packages exempt from individual notification protections (if they post a notification) private final ArraySet<String> mNotificationProtectionExemptPackages = new ArraySet<>(); private final ListenerSet<Runnable> mListeners = new ListenerSet<>(); private volatile MediaProjectionInfo mProjection; private ArraySet<RoleHolder> mNotificationProtectionExemptByRolePackages = new ArraySet<>(); private SensitiveNotificatioMediaProjectionSession mActiveMediaProjectionSession; boolean mDisableScreenShareProtections = false; Loading Loading @@ -128,6 +135,27 @@ public class SensitiveNotificationProtectionControllerImpl } }; @VisibleForTesting final OnRoleHoldersChangedListener mRoleHoldersChangedListener = new OnRoleHoldersChangedListener() { @Override public void onRoleHoldersChanged(@NonNull String roleName, @NonNull UserHandle user) { if (!roleName.equals(AssociationRequest.DEVICE_PROFILE_APP_STREAMING)) { return; } List<String> appStreamingRoleHolders = mRoleManager.getRoleHoldersAsUser( roleName, user); ArraySet<RoleHolder> roleHolders = new ArraySet<>(); for (String appStreamingRoleHolder : appStreamingRoleHolders) { RoleHolder roleHolder = new RoleHolder(appStreamingRoleHolder, user); roleHolders.add(roleHolder); } mNotificationProtectionExemptByRolePackages = roleHolders; } }; private void logSensitiveContentProtectionSessionStart( long sessionId, int projectionAppUid, boolean exempt) { mActiveMediaProjectionSession = Loading Loading @@ -166,11 +194,13 @@ public class SensitiveNotificationProtectionControllerImpl IActivityManager activityManager, PackageManager packageManager, TelephonyManager telephonyManager, RoleManager roleManager, @Main Handler mainHandler, @Background Executor bgExecutor, SensitiveNotificationProtectionControllerLogger logger) { mLogger = logger; mPackageManager = packageManager; mRoleManager = roleManager; if (!screenshareNotificationHiding()) { return; Loading Loading @@ -215,6 +245,8 @@ public class SensitiveNotificationProtectionControllerImpl }); mediaProjectionManager.addCallback(mMediaProjectionCallback, mainHandler); roleManager.addOnRoleHoldersChangedListenerAsUser(bgExecutor, mRoleHoldersChangedListener, UserHandle.ALL); } @NonNull Loading Loading @@ -314,6 +346,10 @@ public class SensitiveNotificationProtectionControllerImpl Log.w(LOG_TAG, "Screen share protections exempt for package " + info.getPackageName() + " via permission"); return null; } else if (info != null && isAppStreamingRoleHolder(info)) { Log.w(LOG_TAG, "Screen share protections exempt for package " + info.getPackageName() + " via role(s) held"); 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 Loading @@ -323,6 +359,11 @@ public class SensitiveNotificationProtectionControllerImpl return info; } private boolean isAppStreamingRoleHolder(@NonNull MediaProjectionInfo info) { return mNotificationProtectionExemptByRolePackages.contains( new RoleHolder(info.getPackageName(), info.getUserHandle())); } private boolean canRecordSensitiveContent(@NonNull String packageName) { // RECORD_SENSITIVE_CONTENT is flagged api on sensitiveNotificationAppProtection if (sensitiveNotificationAppProtection()) { Loading Loading @@ -382,4 +423,26 @@ public class SensitiveNotificationProtectionControllerImpl boolean userForcesRedaction = entry.isChannelVisibilityPrivate(); return notificationRequestsRedaction || userForcesRedaction; } private static final class RoleHolder { private final String mPackageName; private final UserHandle mUserHandle; RoleHolder(String packageName, UserHandle userHandle) { mPackageName = packageName; mUserHandle = userHandle; } @Override public boolean equals(Object o) { if (!(o instanceof RoleHolder that)) return false; return Objects.equals(mPackageName, that.mPackageName) && Objects.equals( mUserHandle, that.mUserHandle); } @Override public int hashCode() { return Objects.hash(mPackageName, mUserHandle); } } }
packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerTest.kt +47 −19 Original line number Diff line number Diff line Loading @@ -25,6 +25,9 @@ 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.app.role.OnRoleHoldersChangedListener import android.app.role.RoleManager import android.companion.AssociationRequest import android.content.pm.PackageManager import android.media.projection.MediaProjectionInfo import android.media.projection.MediaProjectionManager Loading Loading @@ -89,6 +92,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { @Mock private lateinit var activityManager: IActivityManager @Mock private lateinit var mediaProjectionManager: MediaProjectionManager @Mock private lateinit var packageManager: PackageManager @Mock private lateinit var roleManager: RoleManager @Mock private lateinit var telephonyManager: TelephonyManager @Mock private lateinit var listener1: Runnable @Mock private lateinit var listener2: Runnable Loading @@ -98,6 +102,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { private lateinit var executor: FakeExecutor private lateinit var globalSettings: FakeGlobalSettings private lateinit var mediaProjectionCallback: MediaProjectionManager.Callback private lateinit var roleHolderCallback: OnRoleHoldersChangedListener private lateinit var controller: SensitiveNotificationProtectionControllerImpl private lateinit var mediaProjectionInfo: MediaProjectionInfo Loading @@ -117,14 +122,14 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { whenever( packageManager.getPackageUidAsUser( TEST_PROJECTION_PACKAGE_NAME, UserHandle.CURRENT.identifier UserHandle.CURRENT.identifier, ) ) .thenReturn(TEST_PROJECTION_PACKAGE_UID) whenever( packageManager.getPackageUidAsUser( BUGREPORT_PACKAGE_NAME, UserHandle.CURRENT.identifier UserHandle.CURRENT.identifier, ) ) .thenReturn(BUGREPORT_PACKAGE_UID) Loading @@ -134,7 +139,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { whenever( packageManager.getPackageUidAsUser( mContext.packageName, UserHandle.CURRENT.identifier UserHandle.CURRENT.identifier, ) ) .thenReturn(mContext.applicationInfo.uid) Loading @@ -155,9 +160,10 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { activityManager, packageManager, telephonyManager, roleManager, mockExecutorHandler(executor), executor, logger logger, ) // Process pending work (getting global setting and list of exemptions) Loading @@ -167,6 +173,9 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { mediaProjectionCallback = withArgCaptor { verify(mediaProjectionManager).addCallback(capture(), any()) } roleHolderCallback = withArgCaptor { verify(roleManager).addOnRoleHoldersChangedListenerAsUser(any(), capture(), any()) } } @After Loading Loading @@ -307,7 +316,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { whenever( packageManager.checkPermission( android.Manifest.permission.RECORD_SENSITIVE_CONTENT, mediaProjectionInfo.packageName mediaProjectionInfo.packageName, ) ) .thenReturn(PackageManager.PERMISSION_GRANTED) Loading @@ -322,7 +331,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { whenever( packageManager.checkPermission( android.Manifest.permission.RECORD_SENSITIVE_CONTENT, mediaProjectionInfo.packageName mediaProjectionInfo.packageName, ) ) .thenReturn(PackageManager.PERMISSION_GRANTED) Loading @@ -339,6 +348,25 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { assertFalse(controller.isSensitiveStateActive) } @Test fun isSensitiveStateActive_projectionActive_appStreamingRoleHolderExempt_false() { setShareFullScreen() whenever( roleManager.getRoleHoldersAsUser( AssociationRequest.DEVICE_PROFILE_APP_STREAMING, mediaProjectionInfo.userHandle, ) ) .thenReturn(listOf(TEST_PROJECTION_PACKAGE_NAME)) roleHolderCallback.onRoleHoldersChanged( AssociationRequest.DEVICE_PROFILE_APP_STREAMING, mediaProjectionInfo.userHandle, ) mediaProjectionCallback.onStart(mediaProjectionInfo) assertFalse(controller.isSensitiveStateActive) } @Test fun isSensitiveStateActive_projectionActive_disabledViaDevOption_false() { setDisabledViaDeveloperOption() Loading Loading @@ -449,7 +477,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { whenever( packageManager.checkPermission( android.Manifest.permission.RECORD_SENSITIVE_CONTENT, mediaProjectionInfo.packageName mediaProjectionInfo.packageName, ) ) .thenReturn(PackageManager.PERMISSION_GRANTED) Loading @@ -466,7 +494,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { whenever( packageManager.checkPermission( android.Manifest.permission.RECORD_SENSITIVE_CONTENT, mediaProjectionInfo.packageName mediaProjectionInfo.packageName, ) ) .thenReturn(PackageManager.PERMISSION_GRANTED) Loading Loading @@ -528,7 +556,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { eq(TEST_PROJECTION_PACKAGE_UID), eq(false), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__STATE__START), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI) eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI), ) } Loading @@ -541,7 +569,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { eq(TEST_PROJECTION_PACKAGE_UID), eq(false), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__STATE__STOP), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI) eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI), ) } } Loading @@ -559,7 +587,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { eq(TEST_PROJECTION_PACKAGE_UID), eq(true), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__STATE__START), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI) eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI), ) } Loading @@ -572,7 +600,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { eq(TEST_PROJECTION_PACKAGE_UID), eq(true), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__STATE__STOP), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI) eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI), ) } } Loading @@ -590,7 +618,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { eq(TEST_PROJECTION_PACKAGE_UID), eq(true), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__STATE__START), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI) eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI), ) } Loading @@ -603,7 +631,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { eq(TEST_PROJECTION_PACKAGE_UID), eq(true), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__STATE__STOP), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI) eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI), ) } } Loading @@ -623,7 +651,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { eq(mContext.applicationInfo.uid), eq(true), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__STATE__START), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI) eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI), ) } Loading @@ -636,7 +664,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { eq(mContext.applicationInfo.uid), eq(true), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__STATE__STOP), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI) eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI), ) } } Loading @@ -654,7 +682,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { eq(BUGREPORT_PACKAGE_UID), eq(true), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__STATE__START), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI) eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI), ) } Loading @@ -667,7 +695,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { eq(BUGREPORT_PACKAGE_UID), eq(true), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__STATE__STOP), eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI) eq(FrameworkStatsLog.SENSITIVE_CONTENT_MEDIA_PROJECTION_SESSION__SOURCE__SYS_UI), ) } } Loading Loading @@ -757,7 +785,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { return setupNotificationEntry( packageName, overrideVisibility = true, overrideChannelVisibility = true overrideChannelVisibility = true, ) } Loading
services/core/java/com/android/server/SensitiveContentProtectionManagerService.java +18 −3 Original line number Diff line number Diff line Loading @@ -31,6 +31,8 @@ import static com.android.server.wm.WindowManagerInternal.OnWindowRemovedListene import android.annotation.NonNull; import android.annotation.Nullable; import android.app.role.RoleManager; import android.companion.AssociationRequest; import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManager; Loading Loading @@ -81,6 +83,8 @@ public final class SensitiveContentProtectionManagerService extends SystemServic private PackageManagerInternal mPackageManagerInternal; private RoleManager mRoleManager; @Nullable private WindowManagerInternal mWindowManager; Loading Loading @@ -225,7 +229,8 @@ public final class SensitiveContentProtectionManagerService extends SystemServic } @Override public void onStart() {} public void onStart() { } @Override public void onBootPhase(int phase) { Loading @@ -237,6 +242,7 @@ public final class SensitiveContentProtectionManagerService extends SystemServic init(getContext().getSystemService(MediaProjectionManager.class), LocalServices.getService(WindowManagerInternal.class), LocalServices.getService(PackageManagerInternal.class), getContext().getSystemService(RoleManager.class), getExemptedPackages() ); if (sensitiveContentAppProtection()) { Loading @@ -247,7 +253,8 @@ public final class SensitiveContentProtectionManagerService extends SystemServic @VisibleForTesting void init(MediaProjectionManager projectionManager, WindowManagerInternal windowManager, PackageManagerInternal packageManagerInternal, ArraySet<String> exemptedPackages) { PackageManagerInternal packageManagerInternal, RoleManager roleManager, ArraySet<String> exemptedPackages) { if (DEBUG) Log.d(TAG, "init"); Objects.requireNonNull(projectionManager); Loading @@ -256,6 +263,7 @@ public final class SensitiveContentProtectionManagerService extends SystemServic mProjectionManager = projectionManager; mWindowManager = windowManager; mPackageManagerInternal = packageManagerInternal; mRoleManager = roleManager; mExemptedPackages = exemptedPackages; // TODO(b/317250444): use MediaProjectionManagerService directly, reduces unnecessary Loading Loading @@ -312,6 +320,8 @@ public final class SensitiveContentProtectionManagerService extends SystemServic boolean isPackageExempted = (mExemptedPackages != null && mExemptedPackages.contains( projectionInfo.getPackageName())) || canRecordSensitiveContent(projectionInfo.getPackageName()) || holdsAppStreamingRole(projectionInfo.getPackageName(), projectionInfo.getUserHandle()) || isAutofillServiceRecorderPackage(projectionInfo.getUserHandle().getIdentifier(), projectionInfo.getPackageName()); // TODO(b/324447419): move GlobalSettings lookup to background thread Loading Loading @@ -348,6 +358,11 @@ public final class SensitiveContentProtectionManagerService extends SystemServic } } private boolean holdsAppStreamingRole(String packageName, UserHandle userHandle) { return mRoleManager.getRoleHoldersAsUser( AssociationRequest.DEVICE_PROFILE_APP_STREAMING, userHandle).contains(packageName); } private void onProjectionEnd() { synchronized (mSensitiveContentProtectionLock) { mProjectionActive = false; Loading
services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceContentTest.java +21 −2 Original line number Diff line number Diff line Loading @@ -26,7 +26,10 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.role.RoleManager; import android.companion.AssociationRequest; import android.content.pm.PackageManagerInternal; import android.media.projection.MediaProjectionInfo; import android.media.projection.MediaProjectionManager; Loading Loading @@ -54,6 +57,7 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.List; import java.util.Set; @SmallTest Loading @@ -74,6 +78,7 @@ public class SensitiveContentProtectionManagerServiceContentTest { @Mock private WindowManagerInternal mWindowManager; @Mock private MediaProjectionManager mProjectionManager; @Mock private PackageManagerInternal mPackageManagerInternal; @Mock private RoleManager mRoleManager; private MediaProjectionInfo mMediaProjectionInfo; @Captor Loading @@ -93,7 +98,8 @@ public class SensitiveContentProtectionManagerServiceContentTest { mSensitiveContentProtectionManagerService = new SensitiveContentProtectionManagerService(mContext); mSensitiveContentProtectionManagerService.init(mProjectionManager, mWindowManager, mPackageManagerInternal, new ArraySet<>(Set.of(mExemptedScreenRecorderPackage))); mPackageManagerInternal, mRoleManager, new ArraySet<>(Set.of(mExemptedScreenRecorderPackage))); verify(mProjectionManager).addCallback(mMediaProjectionCallbackCaptor.capture(), any()); mMediaPorjectionCallback = mMediaProjectionCallbackCaptor.getValue(); mMediaProjectionInfo = Loading Loading @@ -169,6 +175,19 @@ public class SensitiveContentProtectionManagerServiceContentTest { verify(mWindowManager, never()).addBlockScreenCaptureForApps(mPackageInfoCaptor.capture()); } @Test public void testAppStreamingRoleHolderExemption() { when(mRoleManager.getRoleHoldersAsUser( AssociationRequest.DEVICE_PROFILE_APP_STREAMING, mMediaProjectionInfo.getUserHandle())).thenReturn( List.of(mMediaProjectionInfo.getPackageName())); mMediaPorjectionCallback.onStart(mMediaProjectionInfo); mSensitiveContentProtectionManagerService.setSensitiveContentProtection( mPackageInfo.getWindowToken(), mPackageInfo.getPkg(), mPackageInfo.getUid(), true); verify(mWindowManager, never()).addBlockScreenCaptureForApps(mPackageInfoCaptor.capture()); } private void mockDisabledViaDeveloperOption() { Settings.Global.putInt( mContext.getContentResolver(), Loading