diff options
5 files changed, 154 insertions, 19 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java index 2a4753def463..9916ef6ff9ee 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java @@ -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()) { @@ -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() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java index 8678f0aad181..e111525285e1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java @@ -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 { 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 2b0a92c6ecd7..6956a7d8a8e3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerImpl.java @@ -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; } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java index ccc9dc0d9618..8a48fe10d7fc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java @@ -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; @@ -280,6 +280,66 @@ public class NotificationEntryTest extends SysuiTestCase { } @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() .setName("name") 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 1dac642836c6..a2af38f77f41 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 @@ -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 @@ -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) @@ -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 { |