Loading packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerImpl.java +72 −18 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import android.os.RemoteException; import android.os.Trace; import android.os.UserHandle; import android.service.notification.StatusBarNotification; import android.telephony.TelephonyManager; import android.util.ArraySet; import android.util.Log; Loading Loading @@ -62,7 +63,10 @@ public class SensitiveNotificationProtectionControllerImpl private static final String LOG_TAG = "SNPC"; private final SensitiveNotificationProtectionControllerLogger mLogger; private final PackageManager mPackageManager; private final ArraySet<String> mExemptPackages = new ArraySet<>(); // 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 SensitiveNotificatioMediaProjectionSession mActiveMediaProjectionSession; Loading Loading @@ -161,6 +165,7 @@ public class SensitiveNotificationProtectionControllerImpl MediaProjectionManager mediaProjectionManager, IActivityManager activityManager, PackageManager packageManager, TelephonyManager telephonyManager, @Main Handler mainHandler, @Background Executor bgExecutor, SensitiveNotificationProtectionControllerLogger logger) { Loading Loading @@ -191,26 +196,18 @@ public class SensitiveNotificationProtectionControllerImpl bgExecutor.execute(() -> developerOptionsObserver.onChange(true)); bgExecutor.execute(() -> { ArraySet<String> exemptPackages = new ArraySet<>(); // Exempt SystemUI exemptPackages.add(context.getPackageName()); ArraySet<String> sessionProtectionExemptPackages = getSessionProtectionExemptPackages(context, activityManager); // Exempt approved bug report handlers try { exemptPackages.addAll(activityManager.getBugreportWhitelistedPackages()); } catch (RemoteException e) { Log.e( LOG_TAG, "Error adding bug report handlers to exemption, continuing without", e); // silent failure, skip adding packages to exemption } ArraySet<String> notificationProtectionExemptPackages = getNotificationProtectionExemptPackages(telephonyManager); // if currently projecting, notify listeners of exemption changes mainHandler.post(() -> { Trace.beginSection("SNPC.exemptPackagesUpdated"); try { updateExemptPackagesAndNotifyListeners(exemptPackages); updateExemptPackagesAndNotifyListeners(sessionProtectionExemptPackages, notificationProtectionExemptPackages); } finally { Trace.endSection(); } Loading @@ -220,15 +217,66 @@ public class SensitiveNotificationProtectionControllerImpl mediaProjectionManager.addCallback(mMediaProjectionCallback, mainHandler); } @NonNull private static ArraySet<String> getSessionProtectionExemptPackages(Context context, IActivityManager activityManager) { ArraySet<String> sessionProtectionExemptPackages = new ArraySet<>(); // Exempt SystemUI sessionProtectionExemptPackages.add(context.getPackageName()); // Exempt approved bug report handlers try { sessionProtectionExemptPackages.addAll( activityManager.getBugreportWhitelistedPackages()); } catch (RemoteException e) { Log.w( LOG_TAG, "Error adding bug report handlers to exemption, continuing without", e); // silent failure, skip adding packages to exemption } return sessionProtectionExemptPackages; } @NonNull private static ArraySet<String> getNotificationProtectionExemptPackages( TelephonyManager telephonyManager) { ArraySet<String> notificationProtectionExemptPackages = new ArraySet<>(); // Get Emergency Assistance Package, all notifications from this package should not be // hidden/redacted. if (screenshareNotificationHidingBugFix()) { try { String emergencyAssistancePackageName = telephonyManager.getEmergencyAssistancePackageName(); if (emergencyAssistancePackageName != null) { notificationProtectionExemptPackages.add(emergencyAssistancePackageName); } } catch (IllegalStateException e) { Log.w( LOG_TAG, "Error adding emergency assistance package to exemption", e); // silent failure, skip adding packages to exemption } } return notificationProtectionExemptPackages; } /** * Notify listeners of possible ProjectionState change regardless of current * isSensitiveStateActive value. Method used to ensure updates occur after mExemptPackages gets * updated, which directly changes the outcome of isSensitiveStateActive */ @MainThread private void updateExemptPackagesAndNotifyListeners(ArraySet<String> exemptPackages) { private void updateExemptPackagesAndNotifyListeners( ArraySet<String> sessionProtectionExemptPackages, ArraySet<String> notificationProtectionExemptPackages) { Assert.isMainThread(); mExemptPackages.addAll(exemptPackages); mSessionProtectionExemptPackages.addAll(sessionProtectionExemptPackages); if (screenshareNotificationHidingBugFix()) { mNotificationProtectionExemptPackages.addAll(notificationProtectionExemptPackages); } if (mProjection != null) { updateProjectionStateAndNotifyListeners(mProjection); Loading Loading @@ -258,7 +306,8 @@ public class SensitiveNotificationProtectionControllerImpl if (mDisableScreenShareProtections) { Log.w(LOG_TAG, "Screen share protections disabled"); return null; } else if (info != null && mExemptPackages.contains(info.getPackageName())) { } else if (info != null && mSessionProtectionExemptPackages.contains(info.getPackageName())) { Log.w(LOG_TAG, "Screen share protections exempt for package " + info.getPackageName()); return null; } else if (info != null && canRecordSensitiveContent(info.getPackageName())) { Loading Loading @@ -322,6 +371,11 @@ public class SensitiveNotificationProtectionControllerImpl return false; // do not hide/redact notifications from system uid } if (screenshareNotificationHidingBugFix() && mNotificationProtectionExemptPackages.contains(sbn.getPackageName())) { return false; // do not hide/redact notifications from emergency app } // Only protect/redact notifications if the developer has not explicitly set notification // visibility as public and users has not adjusted default channel visibility to private boolean notificationRequestsRedaction = entry.isNotificationVisibilityPrivate(); Loading packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerFlagDisabledTest.kt +3 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.content.pm.PackageManager import android.media.projection.MediaProjectionManager import android.os.Handler import android.platform.test.annotations.DisableFlags import android.telephony.TelephonyManager import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.server.notification.Flags Loading @@ -46,6 +47,7 @@ class SensitiveNotificationProtectionControllerFlagDisabledTest : SysuiTestCase( @Mock private lateinit var activityManager: IActivityManager @Mock private lateinit var mediaProjectionManager: MediaProjectionManager @Mock private lateinit var packageManager: PackageManager @Mock private lateinit var telephonyManager: TelephonyManager private lateinit var controller: SensitiveNotificationProtectionControllerImpl @Before Loading @@ -59,6 +61,7 @@ class SensitiveNotificationProtectionControllerFlagDisabledTest : SysuiTestCase( mediaProjectionManager, activityManager, packageManager, telephonyManager, handler, FakeExecutor(FakeSystemClock()), logger Loading packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerTest.kt +27 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ 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.telephony.TelephonyManager import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest Loading Loading @@ -89,6 +90,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 telephonyManager: TelephonyManager @Mock private lateinit var listener1: Runnable @Mock private lateinit var listener2: Runnable @Mock private lateinit var listener3: Runnable Loading Loading @@ -141,6 +143,9 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { whenever(packageManager.checkPermission(anyString(), anyString())) .thenReturn(PackageManager.PERMISSION_DENIED) whenever(telephonyManager.getEmergencyAssistancePackageName()) .thenReturn(EMERGENCY_ASSISTANCE_PACKAGE_NAME) executor = FakeExecutor(FakeSystemClock()) globalSettings = FakeGlobalSettings() controller = Loading @@ -150,6 +155,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { mediaProjectionManager, activityManager, packageManager, telephonyManager, mockExecutorHandler(executor), executor, logger Loading Loading @@ -406,6 +412,26 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { assertFalse(controller.shouldProtectNotification(notificationEntry)) } @Test @DisableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING_BUG_FIX) fun shouldProtectNotification_projectionActive_isFromEmergencyPackage_fixDisabled_true() { mediaProjectionCallback.onStart(mediaProjectionInfo) val notificationEntry = setupNotificationEntry(EMERGENCY_ASSISTANCE_PACKAGE_NAME) assertTrue(controller.shouldProtectNotification(notificationEntry)) } @Test @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING_BUG_FIX) fun shouldProtectNotification_projectionActive_isFromEmergencyPackage_false() { mediaProjectionCallback.onStart(mediaProjectionInfo) val notificationEntry = setupNotificationEntry(EMERGENCY_ASSISTANCE_PACKAGE_NAME) assertFalse(controller.shouldProtectNotification(notificationEntry)) } @Test fun shouldProtectNotification_projectionActive_sysuiExempt_false() { // SystemUi context package name is exempt, but in test scenarios its Loading Loading @@ -742,6 +768,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { private const val TEST_PROJECTION_PACKAGE_NAME = "com.android.systemui.statusbar.policy.projectionpackage" private const val TEST_PACKAGE_NAME = "com.android.systemui.statusbar.policy.testpackage" private const val EMERGENCY_ASSISTANCE_PACKAGE_NAME = "com.android.test.emergencyassistance" private const val BUGREPORT_PACKAGE_NAME = "com.android.test.bugreporthandler" } } Loading
packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerImpl.java +72 −18 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import android.os.RemoteException; import android.os.Trace; import android.os.UserHandle; import android.service.notification.StatusBarNotification; import android.telephony.TelephonyManager; import android.util.ArraySet; import android.util.Log; Loading Loading @@ -62,7 +63,10 @@ public class SensitiveNotificationProtectionControllerImpl private static final String LOG_TAG = "SNPC"; private final SensitiveNotificationProtectionControllerLogger mLogger; private final PackageManager mPackageManager; private final ArraySet<String> mExemptPackages = new ArraySet<>(); // 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 SensitiveNotificatioMediaProjectionSession mActiveMediaProjectionSession; Loading Loading @@ -161,6 +165,7 @@ public class SensitiveNotificationProtectionControllerImpl MediaProjectionManager mediaProjectionManager, IActivityManager activityManager, PackageManager packageManager, TelephonyManager telephonyManager, @Main Handler mainHandler, @Background Executor bgExecutor, SensitiveNotificationProtectionControllerLogger logger) { Loading Loading @@ -191,26 +196,18 @@ public class SensitiveNotificationProtectionControllerImpl bgExecutor.execute(() -> developerOptionsObserver.onChange(true)); bgExecutor.execute(() -> { ArraySet<String> exemptPackages = new ArraySet<>(); // Exempt SystemUI exemptPackages.add(context.getPackageName()); ArraySet<String> sessionProtectionExemptPackages = getSessionProtectionExemptPackages(context, activityManager); // Exempt approved bug report handlers try { exemptPackages.addAll(activityManager.getBugreportWhitelistedPackages()); } catch (RemoteException e) { Log.e( LOG_TAG, "Error adding bug report handlers to exemption, continuing without", e); // silent failure, skip adding packages to exemption } ArraySet<String> notificationProtectionExemptPackages = getNotificationProtectionExemptPackages(telephonyManager); // if currently projecting, notify listeners of exemption changes mainHandler.post(() -> { Trace.beginSection("SNPC.exemptPackagesUpdated"); try { updateExemptPackagesAndNotifyListeners(exemptPackages); updateExemptPackagesAndNotifyListeners(sessionProtectionExemptPackages, notificationProtectionExemptPackages); } finally { Trace.endSection(); } Loading @@ -220,15 +217,66 @@ public class SensitiveNotificationProtectionControllerImpl mediaProjectionManager.addCallback(mMediaProjectionCallback, mainHandler); } @NonNull private static ArraySet<String> getSessionProtectionExemptPackages(Context context, IActivityManager activityManager) { ArraySet<String> sessionProtectionExemptPackages = new ArraySet<>(); // Exempt SystemUI sessionProtectionExemptPackages.add(context.getPackageName()); // Exempt approved bug report handlers try { sessionProtectionExemptPackages.addAll( activityManager.getBugreportWhitelistedPackages()); } catch (RemoteException e) { Log.w( LOG_TAG, "Error adding bug report handlers to exemption, continuing without", e); // silent failure, skip adding packages to exemption } return sessionProtectionExemptPackages; } @NonNull private static ArraySet<String> getNotificationProtectionExemptPackages( TelephonyManager telephonyManager) { ArraySet<String> notificationProtectionExemptPackages = new ArraySet<>(); // Get Emergency Assistance Package, all notifications from this package should not be // hidden/redacted. if (screenshareNotificationHidingBugFix()) { try { String emergencyAssistancePackageName = telephonyManager.getEmergencyAssistancePackageName(); if (emergencyAssistancePackageName != null) { notificationProtectionExemptPackages.add(emergencyAssistancePackageName); } } catch (IllegalStateException e) { Log.w( LOG_TAG, "Error adding emergency assistance package to exemption", e); // silent failure, skip adding packages to exemption } } return notificationProtectionExemptPackages; } /** * Notify listeners of possible ProjectionState change regardless of current * isSensitiveStateActive value. Method used to ensure updates occur after mExemptPackages gets * updated, which directly changes the outcome of isSensitiveStateActive */ @MainThread private void updateExemptPackagesAndNotifyListeners(ArraySet<String> exemptPackages) { private void updateExemptPackagesAndNotifyListeners( ArraySet<String> sessionProtectionExemptPackages, ArraySet<String> notificationProtectionExemptPackages) { Assert.isMainThread(); mExemptPackages.addAll(exemptPackages); mSessionProtectionExemptPackages.addAll(sessionProtectionExemptPackages); if (screenshareNotificationHidingBugFix()) { mNotificationProtectionExemptPackages.addAll(notificationProtectionExemptPackages); } if (mProjection != null) { updateProjectionStateAndNotifyListeners(mProjection); Loading Loading @@ -258,7 +306,8 @@ public class SensitiveNotificationProtectionControllerImpl if (mDisableScreenShareProtections) { Log.w(LOG_TAG, "Screen share protections disabled"); return null; } else if (info != null && mExemptPackages.contains(info.getPackageName())) { } else if (info != null && mSessionProtectionExemptPackages.contains(info.getPackageName())) { Log.w(LOG_TAG, "Screen share protections exempt for package " + info.getPackageName()); return null; } else if (info != null && canRecordSensitiveContent(info.getPackageName())) { Loading Loading @@ -322,6 +371,11 @@ public class SensitiveNotificationProtectionControllerImpl return false; // do not hide/redact notifications from system uid } if (screenshareNotificationHidingBugFix() && mNotificationProtectionExemptPackages.contains(sbn.getPackageName())) { return false; // do not hide/redact notifications from emergency app } // Only protect/redact notifications if the developer has not explicitly set notification // visibility as public and users has not adjusted default channel visibility to private boolean notificationRequestsRedaction = entry.isNotificationVisibilityPrivate(); Loading
packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerFlagDisabledTest.kt +3 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.content.pm.PackageManager import android.media.projection.MediaProjectionManager import android.os.Handler import android.platform.test.annotations.DisableFlags import android.telephony.TelephonyManager import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.server.notification.Flags Loading @@ -46,6 +47,7 @@ class SensitiveNotificationProtectionControllerFlagDisabledTest : SysuiTestCase( @Mock private lateinit var activityManager: IActivityManager @Mock private lateinit var mediaProjectionManager: MediaProjectionManager @Mock private lateinit var packageManager: PackageManager @Mock private lateinit var telephonyManager: TelephonyManager private lateinit var controller: SensitiveNotificationProtectionControllerImpl @Before Loading @@ -59,6 +61,7 @@ class SensitiveNotificationProtectionControllerFlagDisabledTest : SysuiTestCase( mediaProjectionManager, activityManager, packageManager, telephonyManager, handler, FakeExecutor(FakeSystemClock()), logger Loading
packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerTest.kt +27 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ 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.telephony.TelephonyManager import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest Loading Loading @@ -89,6 +90,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 telephonyManager: TelephonyManager @Mock private lateinit var listener1: Runnable @Mock private lateinit var listener2: Runnable @Mock private lateinit var listener3: Runnable Loading Loading @@ -141,6 +143,9 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { whenever(packageManager.checkPermission(anyString(), anyString())) .thenReturn(PackageManager.PERMISSION_DENIED) whenever(telephonyManager.getEmergencyAssistancePackageName()) .thenReturn(EMERGENCY_ASSISTANCE_PACKAGE_NAME) executor = FakeExecutor(FakeSystemClock()) globalSettings = FakeGlobalSettings() controller = Loading @@ -150,6 +155,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { mediaProjectionManager, activityManager, packageManager, telephonyManager, mockExecutorHandler(executor), executor, logger Loading Loading @@ -406,6 +412,26 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { assertFalse(controller.shouldProtectNotification(notificationEntry)) } @Test @DisableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING_BUG_FIX) fun shouldProtectNotification_projectionActive_isFromEmergencyPackage_fixDisabled_true() { mediaProjectionCallback.onStart(mediaProjectionInfo) val notificationEntry = setupNotificationEntry(EMERGENCY_ASSISTANCE_PACKAGE_NAME) assertTrue(controller.shouldProtectNotification(notificationEntry)) } @Test @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING_BUG_FIX) fun shouldProtectNotification_projectionActive_isFromEmergencyPackage_false() { mediaProjectionCallback.onStart(mediaProjectionInfo) val notificationEntry = setupNotificationEntry(EMERGENCY_ASSISTANCE_PACKAGE_NAME) assertFalse(controller.shouldProtectNotification(notificationEntry)) } @Test fun shouldProtectNotification_projectionActive_sysuiExempt_false() { // SystemUi context package name is exempt, but in test scenarios its Loading Loading @@ -742,6 +768,7 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { private const val TEST_PROJECTION_PACKAGE_NAME = "com.android.systemui.statusbar.policy.projectionpackage" private const val TEST_PACKAGE_NAME = "com.android.systemui.statusbar.policy.testpackage" private const val EMERGENCY_ASSISTANCE_PACKAGE_NAME = "com.android.test.emergencyassistance" private const val BUGREPORT_PACKAGE_NAME = "com.android.test.bugreporthandler" } }