Loading packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java +2 −4 Original line number Diff line number Diff line Loading @@ -749,7 +749,7 @@ public class NotificationLockscreenUserManagerImpl implements || isNotifUserRedacted; boolean notificationRequestsRedaction = ent.getSbn().getNotification().visibility == Notification.VISIBILITY_PRIVATE; ent.isNotificationVisibilityPrivate(); boolean userForcesRedaction = packageHasVisibilityOverride(ent.getSbn().getKey()); if (keyguardPrivateNotifications()) { Loading @@ -767,9 +767,7 @@ public class NotificationLockscreenUserManagerImpl implements } NotificationEntry entry = mCommonNotifCollectionLazy.get().getEntry(key); if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) { return entry != null && entry.getRanking().getChannel() != null && entry.getRanking().getChannel().getLockscreenVisibility() == Notification.VISIBILITY_PRIVATE; return entry != null && entry.isChannelVisibilityPrivate(); } else { return entry != null && entry.getRanking().getLockscreenVisibilityOverride() Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +17 −0 Original line number Diff line number Diff line Loading @@ -998,6 +998,23 @@ public final class NotificationEntry extends ListEntry { return style == null ? "nostyle" : style.getSimpleName(); } /** * Return {@code true} if notification's visibility is {@link Notification.VISIBILITY_PRIVATE} */ public boolean isNotificationVisibilityPrivate() { return getSbn().getNotification().visibility == Notification.VISIBILITY_PRIVATE; } /** * Return {@code true} if notification's channel lockscreen visibility is * {@link Notification.VISIBILITY_PRIVATE} */ public boolean isChannelVisibilityPrivate() { return getRanking().getChannel() != null && getRanking().getChannel().getLockscreenVisibility() == Notification.VISIBILITY_PRIVATE; } /** Information about a suggestion that is being edited. */ public static class EditedSuggestionInfo { Loading packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerImpl.java +8 −3 Original line number Diff line number Diff line Loading @@ -221,10 +221,15 @@ public class SensitiveNotificationProtectionControllerImpl // Exempt foreground service notifications from protection in effort to keep screen share // stop actions easily accessible StatusBarNotification sbn = entry.getSbn(); if (sbn.getNotification().isFgsOrUij()) { return !sbn.getPackageName().equals(projection.getPackageName()); if (sbn.getNotification().isFgsOrUij() && sbn.getPackageName().equals(projection.getPackageName())) { return false; } return true; // 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(); boolean userForcesRedaction = entry.isChannelVisibilityPrivate(); return notificationRequestsRedaction || userForcesRedaction; } } packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java +61 −1 Original line number Diff line number Diff line Loading @@ -50,8 +50,8 @@ import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; import com.android.systemui.res.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.res.R; import com.android.systemui.statusbar.RankingBuilder; import com.android.systemui.statusbar.SbnBuilder; import com.android.systemui.util.time.FakeSystemClock; Loading Loading @@ -279,6 +279,66 @@ public class NotificationEntryTest extends SysuiTestCase { assertTrue(mEntry.isStickyAndNotDemoted()); } @Test public void testIsNotificationVisibilityPrivate_true() { assertTrue(mEntry.isNotificationVisibilityPrivate()); } @Test public void testIsNotificationVisibilityPrivate_visibilityPublic_false() { Notification.Builder notification = new Notification.Builder(mContext, "") .setVisibility(Notification.VISIBILITY_PUBLIC) .setSmallIcon(R.drawable.ic_person) .setContentTitle("Title") .setContentText("Text"); NotificationEntry entry = new NotificationEntryBuilder() .setPkg(TEST_PACKAGE_NAME) .setOpPkg(TEST_PACKAGE_NAME) .setUid(TEST_UID) .setChannel(mChannel) .setId(mId++) .setNotification(notification.build()) .setUser(new UserHandle(ActivityManager.getCurrentUser())) .build(); assertFalse(entry.isNotificationVisibilityPrivate()); } @Test public void testIsChannelVisibilityPrivate_true() { assertTrue(mEntry.isChannelVisibilityPrivate()); } @Test public void testIsChannelVisibilityPrivate_visibilityPublic_false() { NotificationChannel channel = new NotificationChannel("id", "name", NotificationChannel.USER_LOCKED_IMPORTANCE); channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); StatusBarNotification sbn = new SbnBuilder().build(); Ranking ranking = new RankingBuilder() .setChannel(channel) .setKey(sbn.getKey()) .build(); NotificationEntry entry = new NotificationEntry(sbn, ranking, mClock.uptimeMillis()); assertFalse(entry.isChannelVisibilityPrivate()); } @Test public void testIsChannelVisibilityPrivate_entryHasNoChannel_false() { StatusBarNotification sbn = new SbnBuilder().build(); Ranking ranking = new RankingBuilder() .setChannel(null) .setKey(sbn.getKey()) .build(); NotificationEntry entry = new NotificationEntry(sbn, ranking, mClock.uptimeMillis()); assertFalse(entry.isChannelVisibilityPrivate()); } @Test public void notificationDataEntry_testIsLastMessageFromReply() { Person.Builder person = new Person.Builder() Loading packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerTest.kt +66 −11 Original line number Diff line number Diff line Loading @@ -19,17 +19,24 @@ package com.android.systemui.statusbar.policy import android.app.ActivityOptions import android.app.IActivityManager import android.app.Notification import android.app.Notification.FLAG_FOREGROUND_SERVICE import android.app.Notification.VISIBILITY_PRIVATE 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.media.projection.MediaProjectionInfo import android.media.projection.MediaProjectionManager import android.platform.test.annotations.EnableFlags import android.provider.Settings.Global.DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS import android.service.notification.StatusBarNotification import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest import com.android.server.notification.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.RankingBuilder import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.concurrency.mockExecutorHandler import com.android.systemui.util.mockito.whenever Loading Loading @@ -316,6 +323,25 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { assertFalse(controller.shouldProtectNotification(notificationEntry)) } @Test fun shouldProtectNotification_projectionActive_publicNotification_false() { mediaProjectionCallback.onStart(mediaProjectionInfo) // App marked notification visibility as public val notificationEntry = setupPublicNotificationEntry(TEST_PROJECTION_PACKAGE_NAME) assertFalse(controller.shouldProtectNotification(notificationEntry)) } @Test fun shouldProtectNotification_projectionActive_publicNotificationUserChannelOverride_true() { mediaProjectionCallback.onStart(mediaProjectionInfo) val notificationEntry = setupPublicNotificationEntryWithUserOverriddenChannel(TEST_PROJECTION_PACKAGE_NAME) assertTrue(controller.shouldProtectNotification(notificationEntry)) } private fun setDisabledViaDeveloperOption() { globalSettings.putInt(DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS, 1) Loading @@ -336,21 +362,50 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { private fun setupNotificationEntry( packageName: String, isFgs: Boolean = false isFgs: Boolean = false, overrideVisibility: Boolean = false, overrideChannelVisibility: Boolean = false, ): NotificationEntry { val notificationEntry = mock(NotificationEntry::class.java) val sbn = mock(StatusBarNotification::class.java) val notification = mock(Notification::class.java) whenever(notificationEntry.sbn).thenReturn(sbn) whenever(sbn.packageName).thenReturn(packageName) whenever(sbn.notification).thenReturn(notification) whenever(notification.isFgsOrUij).thenReturn(isFgs) val notification = Notification() if (isFgs) { notification.flags = notification.flags or FLAG_FOREGROUND_SERVICE } if (overrideVisibility) { // Developer has marked notification as public notification.visibility = VISIBILITY_PUBLIC } val notificationEntry = NotificationEntryBuilder().setNotification(notification).setPkg(packageName).build() val channel = NotificationChannel("1", "1", IMPORTANCE_HIGH) if (overrideChannelVisibility) { // User doesn't allow private notifications at the channel level channel.lockscreenVisibility = VISIBILITY_PRIVATE } notificationEntry.setRanking( RankingBuilder(notificationEntry.ranking) .setChannel(channel) .setVisibilityOverride(VISIBILITY_NO_OVERRIDE) .build() ) return notificationEntry } private fun setupFgsNotificationEntry(packageName: String): NotificationEntry { return setupNotificationEntry(packageName, /* isFgs= */ true) return setupNotificationEntry(packageName, isFgs = true) } private fun setupPublicNotificationEntry(packageName: String): NotificationEntry { return setupNotificationEntry(packageName, overrideVisibility = true) } private fun setupPublicNotificationEntryWithUserOverriddenChannel( packageName: String ): NotificationEntry { return setupNotificationEntry( packageName, overrideVisibility = true, overrideChannelVisibility = true ) } companion object { Loading Loading
packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java +2 −4 Original line number Diff line number Diff line Loading @@ -749,7 +749,7 @@ public class NotificationLockscreenUserManagerImpl implements || isNotifUserRedacted; boolean notificationRequestsRedaction = ent.getSbn().getNotification().visibility == Notification.VISIBILITY_PRIVATE; ent.isNotificationVisibilityPrivate(); boolean userForcesRedaction = packageHasVisibilityOverride(ent.getSbn().getKey()); if (keyguardPrivateNotifications()) { Loading @@ -767,9 +767,7 @@ public class NotificationLockscreenUserManagerImpl implements } NotificationEntry entry = mCommonNotifCollectionLazy.get().getEntry(key); if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) { return entry != null && entry.getRanking().getChannel() != null && entry.getRanking().getChannel().getLockscreenVisibility() == Notification.VISIBILITY_PRIVATE; return entry != null && entry.isChannelVisibilityPrivate(); } else { return entry != null && entry.getRanking().getLockscreenVisibilityOverride() Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +17 −0 Original line number Diff line number Diff line Loading @@ -998,6 +998,23 @@ public final class NotificationEntry extends ListEntry { return style == null ? "nostyle" : style.getSimpleName(); } /** * Return {@code true} if notification's visibility is {@link Notification.VISIBILITY_PRIVATE} */ public boolean isNotificationVisibilityPrivate() { return getSbn().getNotification().visibility == Notification.VISIBILITY_PRIVATE; } /** * Return {@code true} if notification's channel lockscreen visibility is * {@link Notification.VISIBILITY_PRIVATE} */ public boolean isChannelVisibilityPrivate() { return getRanking().getChannel() != null && getRanking().getChannel().getLockscreenVisibility() == Notification.VISIBILITY_PRIVATE; } /** Information about a suggestion that is being edited. */ public static class EditedSuggestionInfo { Loading
packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerImpl.java +8 −3 Original line number Diff line number Diff line Loading @@ -221,10 +221,15 @@ public class SensitiveNotificationProtectionControllerImpl // Exempt foreground service notifications from protection in effort to keep screen share // stop actions easily accessible StatusBarNotification sbn = entry.getSbn(); if (sbn.getNotification().isFgsOrUij()) { return !sbn.getPackageName().equals(projection.getPackageName()); if (sbn.getNotification().isFgsOrUij() && sbn.getPackageName().equals(projection.getPackageName())) { return false; } return true; // 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(); boolean userForcesRedaction = entry.isChannelVisibilityPrivate(); return notificationRequestsRedaction || userForcesRedaction; } }
packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java +61 −1 Original line number Diff line number Diff line Loading @@ -50,8 +50,8 @@ import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; import com.android.systemui.res.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.res.R; import com.android.systemui.statusbar.RankingBuilder; import com.android.systemui.statusbar.SbnBuilder; import com.android.systemui.util.time.FakeSystemClock; Loading Loading @@ -279,6 +279,66 @@ public class NotificationEntryTest extends SysuiTestCase { assertTrue(mEntry.isStickyAndNotDemoted()); } @Test public void testIsNotificationVisibilityPrivate_true() { assertTrue(mEntry.isNotificationVisibilityPrivate()); } @Test public void testIsNotificationVisibilityPrivate_visibilityPublic_false() { Notification.Builder notification = new Notification.Builder(mContext, "") .setVisibility(Notification.VISIBILITY_PUBLIC) .setSmallIcon(R.drawable.ic_person) .setContentTitle("Title") .setContentText("Text"); NotificationEntry entry = new NotificationEntryBuilder() .setPkg(TEST_PACKAGE_NAME) .setOpPkg(TEST_PACKAGE_NAME) .setUid(TEST_UID) .setChannel(mChannel) .setId(mId++) .setNotification(notification.build()) .setUser(new UserHandle(ActivityManager.getCurrentUser())) .build(); assertFalse(entry.isNotificationVisibilityPrivate()); } @Test public void testIsChannelVisibilityPrivate_true() { assertTrue(mEntry.isChannelVisibilityPrivate()); } @Test public void testIsChannelVisibilityPrivate_visibilityPublic_false() { NotificationChannel channel = new NotificationChannel("id", "name", NotificationChannel.USER_LOCKED_IMPORTANCE); channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); StatusBarNotification sbn = new SbnBuilder().build(); Ranking ranking = new RankingBuilder() .setChannel(channel) .setKey(sbn.getKey()) .build(); NotificationEntry entry = new NotificationEntry(sbn, ranking, mClock.uptimeMillis()); assertFalse(entry.isChannelVisibilityPrivate()); } @Test public void testIsChannelVisibilityPrivate_entryHasNoChannel_false() { StatusBarNotification sbn = new SbnBuilder().build(); Ranking ranking = new RankingBuilder() .setChannel(null) .setKey(sbn.getKey()) .build(); NotificationEntry entry = new NotificationEntry(sbn, ranking, mClock.uptimeMillis()); assertFalse(entry.isChannelVisibilityPrivate()); } @Test public void notificationDataEntry_testIsLastMessageFromReply() { Person.Builder person = new Person.Builder() Loading
packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerTest.kt +66 −11 Original line number Diff line number Diff line Loading @@ -19,17 +19,24 @@ package com.android.systemui.statusbar.policy import android.app.ActivityOptions import android.app.IActivityManager import android.app.Notification import android.app.Notification.FLAG_FOREGROUND_SERVICE import android.app.Notification.VISIBILITY_PRIVATE 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.media.projection.MediaProjectionInfo import android.media.projection.MediaProjectionManager import android.platform.test.annotations.EnableFlags import android.provider.Settings.Global.DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS import android.service.notification.StatusBarNotification import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest import com.android.server.notification.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.RankingBuilder import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.concurrency.mockExecutorHandler import com.android.systemui.util.mockito.whenever Loading Loading @@ -316,6 +323,25 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { assertFalse(controller.shouldProtectNotification(notificationEntry)) } @Test fun shouldProtectNotification_projectionActive_publicNotification_false() { mediaProjectionCallback.onStart(mediaProjectionInfo) // App marked notification visibility as public val notificationEntry = setupPublicNotificationEntry(TEST_PROJECTION_PACKAGE_NAME) assertFalse(controller.shouldProtectNotification(notificationEntry)) } @Test fun shouldProtectNotification_projectionActive_publicNotificationUserChannelOverride_true() { mediaProjectionCallback.onStart(mediaProjectionInfo) val notificationEntry = setupPublicNotificationEntryWithUserOverriddenChannel(TEST_PROJECTION_PACKAGE_NAME) assertTrue(controller.shouldProtectNotification(notificationEntry)) } private fun setDisabledViaDeveloperOption() { globalSettings.putInt(DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS, 1) Loading @@ -336,21 +362,50 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() { private fun setupNotificationEntry( packageName: String, isFgs: Boolean = false isFgs: Boolean = false, overrideVisibility: Boolean = false, overrideChannelVisibility: Boolean = false, ): NotificationEntry { val notificationEntry = mock(NotificationEntry::class.java) val sbn = mock(StatusBarNotification::class.java) val notification = mock(Notification::class.java) whenever(notificationEntry.sbn).thenReturn(sbn) whenever(sbn.packageName).thenReturn(packageName) whenever(sbn.notification).thenReturn(notification) whenever(notification.isFgsOrUij).thenReturn(isFgs) val notification = Notification() if (isFgs) { notification.flags = notification.flags or FLAG_FOREGROUND_SERVICE } if (overrideVisibility) { // Developer has marked notification as public notification.visibility = VISIBILITY_PUBLIC } val notificationEntry = NotificationEntryBuilder().setNotification(notification).setPkg(packageName).build() val channel = NotificationChannel("1", "1", IMPORTANCE_HIGH) if (overrideChannelVisibility) { // User doesn't allow private notifications at the channel level channel.lockscreenVisibility = VISIBILITY_PRIVATE } notificationEntry.setRanking( RankingBuilder(notificationEntry.ranking) .setChannel(channel) .setVisibilityOverride(VISIBILITY_NO_OVERRIDE) .build() ) return notificationEntry } private fun setupFgsNotificationEntry(packageName: String): NotificationEntry { return setupNotificationEntry(packageName, /* isFgs= */ true) return setupNotificationEntry(packageName, isFgs = true) } private fun setupPublicNotificationEntry(packageName: String): NotificationEntry { return setupNotificationEntry(packageName, overrideVisibility = true) } private fun setupPublicNotificationEntryWithUserOverriddenChannel( packageName: String ): NotificationEntry { return setupNotificationEntry( packageName, overrideVisibility = true, overrideChannelVisibility = true ) } companion object { Loading