Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt +3 −11 Original line number Diff line number Diff line Loading @@ -19,7 +19,6 @@ import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.NotificationLockscreenUserManager import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.notification.collection.ListEntry import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.PipelineEntry import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider Loading Loading @@ -201,19 +200,13 @@ constructor( private fun shouldHideIfEntrySilent(entry: PipelineEntry): Boolean = when { // TODO(b/410825977): The bundle classifier clobbers the channel that the notification // was posted to, and so we don't know whether any child of a bundle is SECRET. For now // treat all bundles as SECRET. entry !is ListEntry -> true // Show if explicitly high priority (not hidden) highPriorityProvider.isExplicitlyHighPriority(entry) -> false // Ambient notifications are hidden always from lock screen entry.representativeEntry?.isAmbient == true -> true entry.asListEntry()?.representativeEntry?.isAmbient == true -> true // [Now notification is silent] // Hide regardless of parent priority if user wants silent notifs hidden // Always hide if user wants silent notifs hidden hideSilentNotificationsOnLockscreen -> true // Parent priority is high enough to be shown on the lockscreen, do not hide. entry.parent?.let(::shouldHideIfEntrySilent) == false -> false // Show when silent notifications are allowed on lockscreen else -> false } Loading Loading @@ -244,8 +237,7 @@ constructor( // ranking.lockscreenVisibilityOverride contains possibly out of date DPC and Setting // info, and NotificationLockscreenUserManagerImpl is already listening for updates // to those return entry.ranking.channel != null && entry.ranking.channel.lockscreenVisibility == VISIBILITY_SECRET return entry.ranking.channel?.lockscreenVisibility == VISIBILITY_SECRET } override fun dump(pw: PrintWriter, args: Array<out String>) = Loading services/core/java/com/android/server/notification/NotificationManagerService.java +17 −1 Original line number Diff line number Diff line Loading @@ -168,6 +168,7 @@ import static android.service.notification.NotificationListenerService.REASON_US import static android.service.notification.NotificationListenerService.Ranking.RANKING_DEMOTED; import static android.service.notification.NotificationListenerService.Ranking.RANKING_PROMOTED; import static android.service.notification.NotificationListenerService.Ranking.RANKING_UNCHANGED; import static android.service.notification.NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE; import static android.service.notification.NotificationListenerService.TRIM_FULL; import static android.service.notification.NotificationListenerService.TRIM_LIGHT; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; Loading Loading @@ -12226,6 +12227,21 @@ public class NotificationManagerService extends SystemService { smartReplies = null; } } NotificationChannel effectiveChannel = record.getChannel().copy(); if (notificationClassificationUi()) { // special handling for a notification's channel visibility when bundled: if the // notification's original channel had a more strict visibility than the current // channel, or if the current channel has an unspecified visibility, patch that // original visibility into the channel stored in Ranking. if (record.getOriginalChannelVisibility() != VISIBILITY_NO_OVERRIDE) { int currentChannelVis = record.getChannel().getLockscreenVisibility(); if (currentChannelVis == VISIBILITY_NO_OVERRIDE || record.getOriginalChannelVisibility() < currentChannelVis) { effectiveChannel.setLockscreenVisibility( record.getOriginalChannelVisibility()); } } } ranking.populate( key, rankings.size(), Loading @@ -12235,7 +12251,7 @@ public class NotificationManagerService extends SystemService { record.getImportance(), record.getImportanceExplanation(), record.getSbn().getOverrideGroupKey(), record.getChannel(), effectiveChannel, record.getPeopleOverride(), record.getSnoozeCriteria(), record.canShowBadge(), services/core/java/com/android/server/notification/NotificationRecord.java +19 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import android.app.Flags; import android.app.KeyguardManager; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.Person; import android.content.Context; import android.content.pm.PackageManager; Loading Loading @@ -228,6 +229,12 @@ public final class NotificationRecord { // was present at the time of unclassification. private boolean mHadGroupSummaryWhenUnclassified = false; // If this notification was classified into a bundle, this value stores the visibility of the // original channel associated with this notification. If the original channel is set to be // secret, that information should override any more generous visibility settings on the newly // assigned bundle. private int mOriginalChannelVisibility = NotificationManager.VISIBILITY_NO_OVERRIDE; public NotificationRecord(Context context, StatusBarNotification sbn, NotificationChannel channel) { this.sbn = sbn; Loading Loading @@ -809,6 +816,8 @@ public final class NotificationRecord { } if (android.service.notification.Flags.notificationClassification()) { if (signals.containsKey(Adjustment.KEY_TYPE)) { // Store original channel visibility before re-assigning channel setOriginalChannelVisibility(mChannel.getLockscreenVisibility()); updateNotificationChannel(signals.getParcelable(Adjustment.KEY_TYPE, NotificationChannel.class)); EventLogTags.writeNotificationAdjusted(getKey(), Loading @@ -816,6 +825,8 @@ public final class NotificationRecord { mChannel.getId()); } if (signals.containsKey(Adjustment.KEY_UNCLASSIFY)) { // reset original channel visibility as we're returning to the original setOriginalChannelVisibility(NotificationManager.VISIBILITY_NO_OVERRIDE); updateNotificationChannel(signals.getParcelable(Adjustment.KEY_UNCLASSIFY, NotificationChannel.class)); EventLogTags.writeNotificationAdjusted(getKey(), Loading Loading @@ -1675,6 +1686,14 @@ public final class NotificationRecord { mHadGroupSummaryWhenUnclassified = exists; } public int getOriginalChannelVisibility() { return mOriginalChannelVisibility; } public void setOriginalChannelVisibility(int visibility) { mOriginalChannelVisibility = visibility; } /** * Whether this notification is a conversation notification. */ Loading services/core/java/com/android/server/notification/VisibilityExtractor.java +4 −2 Original line number Diff line number Diff line Loading @@ -57,7 +57,9 @@ public class VisibilityExtractor implements NotificationSignalExtractor { mConfig.canShowNotificationsOnLockscreen(userId); boolean dpmCanShowNotifications = adminAllowsKeyguardFeature(userId, DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS); boolean channelCanShowNotifications = record.getChannel().getLockscreenVisibility() boolean channelCanShowNotifications = record.getChannel().getLockscreenVisibility() != Notification.VISIBILITY_SECRET && record.getOriginalChannelVisibility() != Notification.VISIBILITY_SECRET; if (!userCanShowNotifications || !dpmCanShowNotifications Loading services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +75 −0 Original line number Diff line number Diff line Loading @@ -81,6 +81,7 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR; import static android.app.NotificationManager.VISIBILITY_NO_OVERRIDE; import static android.app.PendingIntent.FLAG_IMMUTABLE; import static android.app.PendingIntent.FLAG_MUTABLE; import static android.app.PendingIntent.FLAG_ONE_SHOT; Loading Loading @@ -18315,6 +18316,80 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertThat(r.getChannel().getId()).isEqualTo(NEWS_ID); } @Test @EnableFlags({android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION, android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI, FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION}) public void testApplyAdjustment_keyType_storesOriginalChannelVisibility() throws Exception { NotificationManagerService.WorkerHandler handler = mock( NotificationManagerService.WorkerHandler.class); mService.setHandler(handler); when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true); when(mAssistants.isClassificationTypeAllowed(anyInt(), anyInt())).thenReturn(true); when(mAssistants.isAdjustmentAllowedForPackage(anyInt(), anyString(), anyString())).thenReturn(true); NotificationChannel secret = new NotificationChannel("secretChannelId", "secret channel", NotificationManager.IMPORTANCE_DEFAULT); mBinderService.createNotificationChannels(mPkg, new ParceledListSlice(List.of(secret))); // Need to set the visibility as an update since this isn't a field typically set by apps secret.setLockscreenVisibility(Notification.VISIBILITY_SECRET); mBinderService.updateNotificationChannelForPackage(mPkg, mUid, secret); final NotificationRecord r = generateNotificationRecord(secret); mService.addNotification(r); Bundle signals = new Bundle(); signals.putInt(KEY_TYPE, TYPE_NEWS); Adjustment adjustment = new Adjustment( r.getSbn().getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier()); mBinderService.applyAdjustmentFromAssistant(null, adjustment); waitForIdle(); r.applyAdjustments(); // The notification should be bundled now assertThat(r.getChannel().getId()).isEqualTo(NEWS_ID); // but the original channel visibility is stored assertThat(r.getOriginalChannelVisibility()).isEqualTo(Notification.VISIBILITY_SECRET); // check that the information made it to the ranking update too, via the stored channel: // it's still the "news" channel, but with the stricter visibility applied ManagedServices.ManagedServiceInfo info = mock(ManagedServices.ManagedServiceInfo.class); when(info.enabledAndUserMatches(anyInt())).thenReturn(true); when(info.isSameUser(anyInt())).thenReturn(true); NotificationRankingUpdate nru = mService.makeRankingUpdateLocked(info); NotificationListenerService.Ranking ranking = nru.getRankingMap().getRawRankingObject(r.getKey()); assertThat(ranking.getChannel().getId()).isEqualTo(NEWS_ID); assertThat(ranking.getChannel().getLockscreenVisibility()).isEqualTo( Notification.VISIBILITY_SECRET); // Now un-classify doAnswer(invocationOnMock -> { ((NotificationRecord) invocationOnMock.getArguments()[0]).applyAdjustments(); ((NotificationRecord) invocationOnMock.getArguments()[0]).calculateImportance(); return null; }).when(mRankingHelper).extractSignals(any(NotificationRecord.class)); mService.unclassifyNotification(r.getKey()); mService.handleRankingSort(); // confirm it's unclassified assertThat(r.getChannel().getId()).isEqualTo(secret.getId()); // and that the original channel visibility is reset assertThat(r.getOriginalChannelVisibility()).isEqualTo(VISIBILITY_NO_OVERRIDE); // and the ranking objects will be updated accordingly (the ranking's channel should be the // notification's original channel) NotificationRankingUpdate nru2 = mService.makeRankingUpdateLocked(info); NotificationListenerService.Ranking ranking2 = nru2.getRankingMap().getRawRankingObject(r.getKey()); assertThat(ranking2.getChannel()).isEqualTo(secret); } @Test @EnableFlags({FLAG_API_RICH_ONGOING, android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION, Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt +3 −11 Original line number Diff line number Diff line Loading @@ -19,7 +19,6 @@ import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.NotificationLockscreenUserManager import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.notification.collection.ListEntry import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.PipelineEntry import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider Loading Loading @@ -201,19 +200,13 @@ constructor( private fun shouldHideIfEntrySilent(entry: PipelineEntry): Boolean = when { // TODO(b/410825977): The bundle classifier clobbers the channel that the notification // was posted to, and so we don't know whether any child of a bundle is SECRET. For now // treat all bundles as SECRET. entry !is ListEntry -> true // Show if explicitly high priority (not hidden) highPriorityProvider.isExplicitlyHighPriority(entry) -> false // Ambient notifications are hidden always from lock screen entry.representativeEntry?.isAmbient == true -> true entry.asListEntry()?.representativeEntry?.isAmbient == true -> true // [Now notification is silent] // Hide regardless of parent priority if user wants silent notifs hidden // Always hide if user wants silent notifs hidden hideSilentNotificationsOnLockscreen -> true // Parent priority is high enough to be shown on the lockscreen, do not hide. entry.parent?.let(::shouldHideIfEntrySilent) == false -> false // Show when silent notifications are allowed on lockscreen else -> false } Loading Loading @@ -244,8 +237,7 @@ constructor( // ranking.lockscreenVisibilityOverride contains possibly out of date DPC and Setting // info, and NotificationLockscreenUserManagerImpl is already listening for updates // to those return entry.ranking.channel != null && entry.ranking.channel.lockscreenVisibility == VISIBILITY_SECRET return entry.ranking.channel?.lockscreenVisibility == VISIBILITY_SECRET } override fun dump(pw: PrintWriter, args: Array<out String>) = Loading
services/core/java/com/android/server/notification/NotificationManagerService.java +17 −1 Original line number Diff line number Diff line Loading @@ -168,6 +168,7 @@ import static android.service.notification.NotificationListenerService.REASON_US import static android.service.notification.NotificationListenerService.Ranking.RANKING_DEMOTED; import static android.service.notification.NotificationListenerService.Ranking.RANKING_PROMOTED; import static android.service.notification.NotificationListenerService.Ranking.RANKING_UNCHANGED; import static android.service.notification.NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE; import static android.service.notification.NotificationListenerService.TRIM_FULL; import static android.service.notification.NotificationListenerService.TRIM_LIGHT; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; Loading Loading @@ -12226,6 +12227,21 @@ public class NotificationManagerService extends SystemService { smartReplies = null; } } NotificationChannel effectiveChannel = record.getChannel().copy(); if (notificationClassificationUi()) { // special handling for a notification's channel visibility when bundled: if the // notification's original channel had a more strict visibility than the current // channel, or if the current channel has an unspecified visibility, patch that // original visibility into the channel stored in Ranking. if (record.getOriginalChannelVisibility() != VISIBILITY_NO_OVERRIDE) { int currentChannelVis = record.getChannel().getLockscreenVisibility(); if (currentChannelVis == VISIBILITY_NO_OVERRIDE || record.getOriginalChannelVisibility() < currentChannelVis) { effectiveChannel.setLockscreenVisibility( record.getOriginalChannelVisibility()); } } } ranking.populate( key, rankings.size(), Loading @@ -12235,7 +12251,7 @@ public class NotificationManagerService extends SystemService { record.getImportance(), record.getImportanceExplanation(), record.getSbn().getOverrideGroupKey(), record.getChannel(), effectiveChannel, record.getPeopleOverride(), record.getSnoozeCriteria(), record.canShowBadge(),
services/core/java/com/android/server/notification/NotificationRecord.java +19 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import android.app.Flags; import android.app.KeyguardManager; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.Person; import android.content.Context; import android.content.pm.PackageManager; Loading Loading @@ -228,6 +229,12 @@ public final class NotificationRecord { // was present at the time of unclassification. private boolean mHadGroupSummaryWhenUnclassified = false; // If this notification was classified into a bundle, this value stores the visibility of the // original channel associated with this notification. If the original channel is set to be // secret, that information should override any more generous visibility settings on the newly // assigned bundle. private int mOriginalChannelVisibility = NotificationManager.VISIBILITY_NO_OVERRIDE; public NotificationRecord(Context context, StatusBarNotification sbn, NotificationChannel channel) { this.sbn = sbn; Loading Loading @@ -809,6 +816,8 @@ public final class NotificationRecord { } if (android.service.notification.Flags.notificationClassification()) { if (signals.containsKey(Adjustment.KEY_TYPE)) { // Store original channel visibility before re-assigning channel setOriginalChannelVisibility(mChannel.getLockscreenVisibility()); updateNotificationChannel(signals.getParcelable(Adjustment.KEY_TYPE, NotificationChannel.class)); EventLogTags.writeNotificationAdjusted(getKey(), Loading @@ -816,6 +825,8 @@ public final class NotificationRecord { mChannel.getId()); } if (signals.containsKey(Adjustment.KEY_UNCLASSIFY)) { // reset original channel visibility as we're returning to the original setOriginalChannelVisibility(NotificationManager.VISIBILITY_NO_OVERRIDE); updateNotificationChannel(signals.getParcelable(Adjustment.KEY_UNCLASSIFY, NotificationChannel.class)); EventLogTags.writeNotificationAdjusted(getKey(), Loading Loading @@ -1675,6 +1686,14 @@ public final class NotificationRecord { mHadGroupSummaryWhenUnclassified = exists; } public int getOriginalChannelVisibility() { return mOriginalChannelVisibility; } public void setOriginalChannelVisibility(int visibility) { mOriginalChannelVisibility = visibility; } /** * Whether this notification is a conversation notification. */ Loading
services/core/java/com/android/server/notification/VisibilityExtractor.java +4 −2 Original line number Diff line number Diff line Loading @@ -57,7 +57,9 @@ public class VisibilityExtractor implements NotificationSignalExtractor { mConfig.canShowNotificationsOnLockscreen(userId); boolean dpmCanShowNotifications = adminAllowsKeyguardFeature(userId, DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS); boolean channelCanShowNotifications = record.getChannel().getLockscreenVisibility() boolean channelCanShowNotifications = record.getChannel().getLockscreenVisibility() != Notification.VISIBILITY_SECRET && record.getOriginalChannelVisibility() != Notification.VISIBILITY_SECRET; if (!userCanShowNotifications || !dpmCanShowNotifications Loading
services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +75 −0 Original line number Diff line number Diff line Loading @@ -81,6 +81,7 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR; import static android.app.NotificationManager.VISIBILITY_NO_OVERRIDE; import static android.app.PendingIntent.FLAG_IMMUTABLE; import static android.app.PendingIntent.FLAG_MUTABLE; import static android.app.PendingIntent.FLAG_ONE_SHOT; Loading Loading @@ -18315,6 +18316,80 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertThat(r.getChannel().getId()).isEqualTo(NEWS_ID); } @Test @EnableFlags({android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION, android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI, FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION}) public void testApplyAdjustment_keyType_storesOriginalChannelVisibility() throws Exception { NotificationManagerService.WorkerHandler handler = mock( NotificationManagerService.WorkerHandler.class); mService.setHandler(handler); when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true); when(mAssistants.isClassificationTypeAllowed(anyInt(), anyInt())).thenReturn(true); when(mAssistants.isAdjustmentAllowedForPackage(anyInt(), anyString(), anyString())).thenReturn(true); NotificationChannel secret = new NotificationChannel("secretChannelId", "secret channel", NotificationManager.IMPORTANCE_DEFAULT); mBinderService.createNotificationChannels(mPkg, new ParceledListSlice(List.of(secret))); // Need to set the visibility as an update since this isn't a field typically set by apps secret.setLockscreenVisibility(Notification.VISIBILITY_SECRET); mBinderService.updateNotificationChannelForPackage(mPkg, mUid, secret); final NotificationRecord r = generateNotificationRecord(secret); mService.addNotification(r); Bundle signals = new Bundle(); signals.putInt(KEY_TYPE, TYPE_NEWS); Adjustment adjustment = new Adjustment( r.getSbn().getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier()); mBinderService.applyAdjustmentFromAssistant(null, adjustment); waitForIdle(); r.applyAdjustments(); // The notification should be bundled now assertThat(r.getChannel().getId()).isEqualTo(NEWS_ID); // but the original channel visibility is stored assertThat(r.getOriginalChannelVisibility()).isEqualTo(Notification.VISIBILITY_SECRET); // check that the information made it to the ranking update too, via the stored channel: // it's still the "news" channel, but with the stricter visibility applied ManagedServices.ManagedServiceInfo info = mock(ManagedServices.ManagedServiceInfo.class); when(info.enabledAndUserMatches(anyInt())).thenReturn(true); when(info.isSameUser(anyInt())).thenReturn(true); NotificationRankingUpdate nru = mService.makeRankingUpdateLocked(info); NotificationListenerService.Ranking ranking = nru.getRankingMap().getRawRankingObject(r.getKey()); assertThat(ranking.getChannel().getId()).isEqualTo(NEWS_ID); assertThat(ranking.getChannel().getLockscreenVisibility()).isEqualTo( Notification.VISIBILITY_SECRET); // Now un-classify doAnswer(invocationOnMock -> { ((NotificationRecord) invocationOnMock.getArguments()[0]).applyAdjustments(); ((NotificationRecord) invocationOnMock.getArguments()[0]).calculateImportance(); return null; }).when(mRankingHelper).extractSignals(any(NotificationRecord.class)); mService.unclassifyNotification(r.getKey()); mService.handleRankingSort(); // confirm it's unclassified assertThat(r.getChannel().getId()).isEqualTo(secret.getId()); // and that the original channel visibility is reset assertThat(r.getOriginalChannelVisibility()).isEqualTo(VISIBILITY_NO_OVERRIDE); // and the ranking objects will be updated accordingly (the ranking's channel should be the // notification's original channel) NotificationRankingUpdate nru2 = mService.makeRankingUpdateLocked(info); NotificationListenerService.Ranking ranking2 = nru2.getRankingMap().getRawRankingObject(r.getKey()); assertThat(ranking2.getChannel()).isEqualTo(secret); } @Test @EnableFlags({FLAG_API_RICH_ONGOING, android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION,