summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Steve Elliott <steell@google.com> 2021-10-21 17:19:34 -0400
committer Jeff DeCew <jeffdq@google.com> 2021-10-30 21:49:06 -0400
commit9a4112bfbb08d08baf73f4e99a834eeb06f7a86b (patch)
tree7c05a65a38a35948ea00cf514850380152585bbd
parent91da8a29767ef992f93ee1b96dfc7fec4f4d9ab4 (diff)
Add sensitive content redaction to notif pipeline
This incorporates two behaviors originally handled by the NotificationViewHierarchyManager: 1. A change in DynamicPrivacy would trigger NotificationViewHierarchyManager#updateNotificationViews 2. The aformentioned #updateNotificationViews method was responsible for setting the redaction state on each NotificationEntry A new Coordinator is introduced to the NotifPipeline to handle both of these behaviors. (1) is handled by attaching a new Invalidator that will request a notif list rebuild when dynamic privacy changes. (2) is handled by processing each ListEntry in an OnPreRenderListListener, and setting the redaction state on each NotificationEntry. Fixes: 204127880 Test: manual - turn on setting to hide sensitive notification content on the lockscreen, lock phone, turn screen back on, observe notifications on lock screen Bug: 200025730 Merged-In: I57b4d68f440ea50f13f6da4676144400f875ea8c Change-Id: I57b4d68f440ea50f13f6da4676144400f875ea8c
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt104
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Invalidator.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt209
14 files changed, 425 insertions, 5 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
index 947a39a32365..48bb281429c2 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
@@ -18,6 +18,8 @@ package com.android.systemui.flags;
import android.content.Context;
import android.util.FeatureFlagUtils;
+import android.util.Log;
+import android.widget.Toast;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
@@ -86,6 +88,22 @@ public class FeatureFlags {
}
}
+ public void assertLegacyPipelineEnabled() {
+ if (isNewNotifPipelineRenderingEnabled()) {
+ throw new IllegalStateException("Old pipeline code running w/ new pipeline enabled");
+ }
+ }
+
+ public boolean checkLegacyPipelineEnabled() {
+ if (!isNewNotifPipelineRenderingEnabled()) {
+ return true;
+ }
+ Log.d("NotifPipeline", "Old pipeline code running w/ new pipeline enabled",
+ new Exception());
+ Toast.makeText(mContext, "Old pipeline code running!", Toast.LENGTH_SHORT).show();
+ return false;
+ }
+
public boolean isNewNotifPipelineEnabled() {
return isEnabled(Flags.NEW_NOTIFICATION_PIPELINE);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 396d86bab825..be3364befbe6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -27,6 +27,7 @@ import android.view.ViewGroup;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.dagger.StatusBarModule;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
@@ -72,6 +73,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
// Dependencies:
private final DynamicChildBindController mDynamicChildBindController;
+ private final FeatureFlags mFeatureFlags;
protected final NotificationLockscreenUserManager mLockscreenUserManager;
protected final NotificationGroupManagerLegacy mGroupManager;
protected final VisualStabilityManager mVisualStabilityManager;
@@ -107,6 +109,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
public NotificationViewHierarchyManager(
Context context,
@Main Handler mainHandler,
+ FeatureFlags featureFlags,
NotificationLockscreenUserManager notificationLockscreenUserManager,
NotificationGroupManagerLegacy groupManager,
VisualStabilityManager visualStabilityManager,
@@ -121,6 +124,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
AssistantFeedbackController assistantFeedbackController) {
mContext = context;
mHandler = mainHandler;
+ mFeatureFlags = featureFlags;
mLockscreenUserManager = notificationLockscreenUserManager;
mBypassController = bypassController;
mGroupManager = groupManager;
@@ -151,6 +155,10 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
//TODO: Rewrite this to focus on Entries, or some other data object instead of views
public void updateNotificationViews() {
Assert.isMainThread();
+ if (!mFeatureFlags.checkLegacyPipelineEnabled()) {
+ return;
+ }
+
beginUpdate();
List<NotificationEntry> activeNotifications = mEntryManager.getVisibleNotifications();
@@ -425,6 +433,10 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
*/
public void updateRowStates() {
Assert.isMainThread();
+ if (!mFeatureFlags.checkLegacyPipelineEnabled()) {
+ return;
+ }
+
beginUpdate();
updateRowStatesInternal();
endUpdate();
@@ -510,6 +522,9 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
@Override
public void onDynamicPrivacyChanged() {
+ if (!mFeatureFlags.checkLegacyPipelineEnabled()) {
+ return;
+ }
if (mPerformingUpdate) {
Log.w(TAG, "onDynamicPrivacyChanged made a re-entrant call");
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index d297d9581d6a..1c9174a33bbc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -184,6 +184,7 @@ public interface StatusBarDependenciesModule {
static NotificationViewHierarchyManager provideNotificationViewHierarchyManager(
Context context,
@Main Handler mainHandler,
+ FeatureFlags featureFlags,
NotificationLockscreenUserManager notificationLockscreenUserManager,
NotificationGroupManagerLegacy groupManager,
VisualStabilityManager visualStabilityManager,
@@ -199,6 +200,7 @@ public interface StatusBarDependenciesModule {
return new NotificationViewHierarchyManager(
context,
mainHandler,
+ featureFlags,
notificationLockscreenUserManager,
groupManager,
visualStabilityManager,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java
index 47939f0579f5..577792547c23 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java
@@ -23,6 +23,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.OnBefo
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeSortListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeTransformGroupsListener;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
@@ -216,6 +217,11 @@ public class NotifPipeline implements CommonNotifCollection {
mShadeListBuilder.addOnBeforeRenderListListener(listener);
}
+ /** Registers an invalidator that can be used to invalidate the entire notif list. */
+ public void addPreRenderInvalidator(Invalidator invalidator) {
+ mShadeListBuilder.addPreRenderInvalidator(invalidator);
+ }
+
/**
* Returns a read-only view in to the current shade list, i.e. the list of notifications that
* are currently present in the shade. If this method is called during pipeline execution it
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 3730524353ec..9faec7bac40f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -45,6 +45,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.OnBefo
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeTransformGroupsListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState;
import com.android.systemui.statusbar.notification.collection.listbuilder.ShadeListBuilderLogger;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
@@ -174,6 +175,13 @@ public class ShadeListBuilder implements Dumpable {
mOnBeforeRenderListListeners.add(listener);
}
+ void addPreRenderInvalidator(Invalidator invalidator) {
+ Assert.isMainThread();
+
+ mPipelineState.requireState(STATE_IDLE);
+ invalidator.setInvalidationListener(this::onPreRenderInvalidated);
+ }
+
void addPreGroupFilter(NotifFilter filter) {
Assert.isMainThread();
mPipelineState.requireState(STATE_IDLE);
@@ -256,6 +264,14 @@ public class ShadeListBuilder implements Dumpable {
}
};
+ private void onPreRenderInvalidated(Invalidator invalidator) {
+ Assert.isMainThread();
+
+ mLogger.logPreRenderInvalidated(invalidator.getName(), mPipelineState.getState());
+
+ rebuildListIfBefore(STATE_FINALIZING);
+ }
+
private void onPreGroupFilterInvalidated(NotifFilter filter) {
Assert.isMainThread();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
index 93059009a56e..239161696379 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
@@ -64,7 +64,8 @@ public class NotifCoordinators implements Dumpable {
ShadeEventCoordinator shadeEventCoordinator,
SmartspaceDedupingCoordinator smartspaceDedupingCoordinator,
ViewConfigCoordinator viewConfigCoordinator,
- VisualStabilityCoordinator visualStabilityCoordinator) {
+ VisualStabilityCoordinator visualStabilityCoordinator,
+ SensitiveContentCoordinator sensitiveContentCoordinator) {
dumpManager.registerDumpable(TAG, this);
mCoordinators.add(new HideLocallyDismissedNotifsCoordinator());
@@ -79,6 +80,7 @@ public class NotifCoordinators implements Dumpable {
mCoordinators.add(shadeEventCoordinator);
mCoordinators.add(viewConfigCoordinator);
mCoordinators.add(visualStabilityCoordinator);
+ mCoordinators.add(sensitiveContentCoordinator);
if (featureFlags.isSmartspaceDedupingEnabled()) {
mCoordinators.add(smartspaceDedupingCoordinator);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
new file mode 100644
index 000000000000..2da5d205af95
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator
+
+import android.os.UserHandle
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.NotificationLockscreenUserManager
+import com.android.systemui.statusbar.notification.DynamicPrivacyController
+import com.android.systemui.statusbar.notification.collection.GroupEntry
+import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator
+import dagger.Module
+import dagger.Provides
+
+@Module
+object SensitiveContentCoordinatorModule {
+ @Provides
+ @SysUISingleton
+ fun provideCoordinator(
+ dynamicPrivacyController: DynamicPrivacyController,
+ lockscreenUserManager: NotificationLockscreenUserManager
+ ): SensitiveContentCoordinator =
+ SensitiveContentCoordinatorImpl(dynamicPrivacyController, lockscreenUserManager)
+}
+
+/** Coordinates re-inflation and post-processing of sensitive notification content. */
+interface SensitiveContentCoordinator : Coordinator
+
+private class SensitiveContentCoordinatorImpl(
+ private val dynamicPrivacyController: DynamicPrivacyController,
+ private val lockscreenUserManager: NotificationLockscreenUserManager
+) : Invalidator("SensitiveContentInvalidator"),
+ SensitiveContentCoordinator,
+ DynamicPrivacyController.Listener,
+ OnBeforeRenderListListener {
+
+ override fun attach(pipeline: NotifPipeline) {
+ dynamicPrivacyController.addListener(this)
+ pipeline.addOnBeforeRenderListListener(this)
+ pipeline.addPreRenderInvalidator(this)
+ }
+
+ override fun onDynamicPrivacyChanged(): Unit = invalidateList()
+
+ override fun onBeforeRenderList(entries: List<ListEntry>) {
+ val currentUserId = lockscreenUserManager.currentUserId
+ val devicePublic = lockscreenUserManager.isLockscreenPublicMode(currentUserId)
+ val deviceSensitive = devicePublic &&
+ !lockscreenUserManager.userAllowsPrivateNotificationsInPublic(currentUserId)
+ val dynamicallyUnlocked = dynamicPrivacyController.isDynamicallyUnlocked
+ for (entry in extractAllRepresentativeEntries(entries).filter { it.rowExists() }) {
+ val notifUserId = entry.sbn.user.identifier
+ val userLockscreen = devicePublic ||
+ lockscreenUserManager.isLockscreenPublicMode(notifUserId)
+ val userPublic = when {
+ // if we're not on the lockscreen, we're definitely private
+ !userLockscreen -> false
+ // we are on the lockscreen, so unless we're dynamically unlocked, we're
+ // definitely public
+ !dynamicallyUnlocked -> true
+ // we're dynamically unlocked, but check if the notification needs
+ // a separate challenge if it's from a work profile
+ else -> when (notifUserId) {
+ currentUserId -> false
+ UserHandle.USER_ALL -> false
+ else -> lockscreenUserManager.needsSeparateWorkChallenge(notifUserId)
+ }
+ }
+ val needsRedaction = lockscreenUserManager.needsRedaction(entry)
+ val isSensitive = userPublic && needsRedaction
+ entry.setSensitive(isSensitive, deviceSensitive)
+ }
+ }
+}
+
+private fun extractAllRepresentativeEntries(
+ entries: List<ListEntry>
+): Sequence<NotificationEntry> =
+ entries.asSequence().flatMap(::extractAllRepresentativeEntries)
+
+private fun extractAllRepresentativeEntries(listEntry: ListEntry): Sequence<NotificationEntry> =
+ sequence {
+ listEntry.representativeEntry?.let { yield(it) }
+ if (listEntry is GroupEntry) {
+ yieldAll(extractAllRepresentativeEntries(listEntry.children))
+ }
+ } \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
index 5a35127397b4..8fff90504798 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
@@ -47,6 +47,15 @@ class ShadeListBuilderLogger @Inject constructor(
})
}
+ fun logPreRenderInvalidated(filterName: String, pipelineState: Int) {
+ buffer.log(TAG, DEBUG, {
+ str1 = filterName
+ int1 = pipelineState
+ }, {
+ """Pre-render Invalidator "$str1" invalidated; pipeline state is $int1"""
+ })
+ }
+
fun logPreGroupFilterInvalidated(filterName: String, pipelineState: Int) {
buffer.log(TAG, DEBUG, {
str1 = filterName
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Invalidator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Invalidator.java
new file mode 100644
index 000000000000..d7092ecd536e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Invalidator.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.listbuilder.pluggable;
+
+/** A {@link Pluggable} that can only invalidate. */
+public abstract class Invalidator extends Pluggable<Invalidator> {
+ protected Invalidator(String name) {
+ super(name);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 540216cc662b..c4afc524b5c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -45,6 +45,7 @@ import com.android.systemui.statusbar.notification.NotificationEntryManagerLogge
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.coordinator.SensitiveContentCoordinatorModule;
import com.android.systemui.statusbar.notification.collection.coordinator.ShadeEventCoordinator;
import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator;
import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
@@ -93,7 +94,10 @@ import dagger.Provides;
/**
* Dagger Module for classes found within the com.android.systemui.statusbar.notification package.
*/
-@Module(includes = {NotificationSectionHeadersModule.class})
+@Module(includes = {
+ NotificationSectionHeadersModule.class,
+ SensitiveContentCoordinatorModule.class
+})
public interface NotificationsModule {
@Binds
StackScrollAlgorithm.SectionProvider bindSectionProvider(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index cf9b2c6775b8..ecd5c985154c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -314,9 +314,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
mShadeController.addPostCollapseAction(() -> updateNotificationViews(reason));
return;
}
-
mViewHierarchyManager.updateNotificationViews();
-
mNotificationPanel.updateNotificationViews(reason);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index 7fb7b8667a1b..cf58c63e3d26 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -36,6 +36,7 @@ import android.widget.LinearLayout;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
import com.android.systemui.statusbar.notification.DynamicChildBindController;
@@ -75,6 +76,7 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase {
@Spy private FakeListContainer mListContainer = new FakeListContainer();
// Dependency mocks:
+ @Mock private FeatureFlags mFeatureFlags;
@Mock private NotificationEntryManager mEntryManager;
@Mock private NotificationLockscreenUserManager mLockscreenUserManager;
@Mock private NotificationGroupManagerLegacy mGroupManager;
@@ -101,10 +103,14 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase {
when(mVisualStabilityManager.areGroupChangesAllowed()).thenReturn(true);
when(mVisualStabilityManager.isReorderingAllowed()).thenReturn(true);
+ when(mFeatureFlags.isNewNotifPipelineEnabled()).thenReturn(false);
+ when(mFeatureFlags.checkLegacyPipelineEnabled()).thenReturn(true);
+
mHelper = new NotificationTestHelper(mContext, mDependency, TestableLooper.get(this));
mViewHierarchyManager = new NotificationViewHierarchyManager(mContext,
- mHandler, mLockscreenUserManager, mGroupManager, mVisualStabilityManager,
+ mHandler, mFeatureFlags, mLockscreenUserManager, mGroupManager,
+ mVisualStabilityManager,
mock(StatusBarStateControllerImpl.class), mEntryManager,
mock(KeyguardBypassController.class),
Optional.of(mock(Bubbles.class)),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
index e9e6718f5a5c..190c3521e83c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
@@ -56,6 +56,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.OnBefo
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeSortListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeTransformGroupsListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.ShadeListBuilderLogger;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
@@ -847,11 +848,13 @@ public class ShadeListBuilderTest extends SysuiTestCase {
NotifPromoter idPromoter = new IdPromoter(4);
NotifSectioner section = new PackageSectioner(PACKAGE_1);
NotifComparator hypeComparator = new HypeComparator(PACKAGE_2);
+ Invalidator preRenderInvalidator = new Invalidator("PreRenderInvalidator") {};
mListBuilder.addPreGroupFilter(packageFilter);
mListBuilder.addPromoter(idPromoter);
mListBuilder.setSectioners(singletonList(section));
mListBuilder.setComparators(singletonList(hypeComparator));
+ mListBuilder.addPreRenderInvalidator(preRenderInvalidator);
// GIVEN a set of random notifs
addNotif(0, PACKAGE_1);
@@ -876,6 +879,10 @@ public class ShadeListBuilderTest extends SysuiTestCase {
clearInvocations(mOnRenderListListener);
hypeComparator.invalidateList();
verify(mOnRenderListListener).onRenderList(anyList());
+
+ clearInvocations(mOnRenderListListener);
+ preRenderInvalidator.invalidateList();
+ verify(mOnRenderListListener).onRenderList(anyList());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
new file mode 100644
index 000000000000..5fd4174af164
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator
+
+import android.os.UserHandle
+import android.service.notification.StatusBarNotification
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.NotificationLockscreenUserManager
+import com.android.systemui.statusbar.notification.DynamicPrivacyController
+import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable
+import com.android.systemui.util.mockito.withArgCaptor
+import com.android.systemui.util.mockito.mock
+import org.junit.Test
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+
+@SmallTest
+class SensitiveContentCoordinatorTest : SysuiTestCase() {
+
+ val dynamicPrivacyController: DynamicPrivacyController = mock()
+ val lockscreenUserManager: NotificationLockscreenUserManager = mock()
+ val pipeline: NotifPipeline = mock()
+
+ val coordinator: SensitiveContentCoordinator = SensitiveContentCoordinatorModule
+ .provideCoordinator(dynamicPrivacyController, lockscreenUserManager)
+
+ @Test
+ fun onDynamicPrivacyChanged_invokeInvalidationListener() {
+ coordinator.attach(pipeline)
+ val invalidator = withArgCaptor<Invalidator> {
+ verify(pipeline).addPreRenderInvalidator(capture())
+ }
+ val dynamicPrivacyListener = withArgCaptor<DynamicPrivacyController.Listener> {
+ verify(dynamicPrivacyController).addListener(capture())
+ }
+
+ val invalidationListener = mock<Pluggable.PluggableListener<Invalidator>>()
+ invalidator.setInvalidationListener(invalidationListener)
+
+ dynamicPrivacyListener.onDynamicPrivacyChanged()
+
+ verify(invalidationListener).onPluggableInvalidated(invalidator)
+ }
+
+ @Test
+ fun onBeforeRenderList_deviceUnlocked_notifDoesNotNeedRedaction() {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener = withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
+
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(true)
+ whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
+ val entry = fakeNotification(1, false)
+
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+
+ verify(entry.representativeEntry!!).setSensitive(false, false)
+ }
+
+ @Test
+ fun onBeforeRenderList_deviceUnlocked_notifWouldNeedRedaction() {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener = withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
+
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(false)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(true)
+ whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
+ val entry = fakeNotification(1, true)
+
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+
+ verify(entry.representativeEntry!!).setSensitive(false, false)
+ }
+
+ @Test
+ fun onBeforeRenderList_deviceLocked_userAllowsPublicNotifs() {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener = withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
+
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(true)
+ whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
+ val entry = fakeNotification(1, false)
+
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+
+ verify(entry.representativeEntry!!).setSensitive(false, false)
+ }
+
+ @Test
+ fun onBeforeRenderList_deviceLocked_userDisallowsPublicNotifs_notifDoesNotNeedRedaction() {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener = withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
+
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false)
+ whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
+ val entry = fakeNotification(1, false)
+
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+
+ verify(entry.representativeEntry!!).setSensitive(false, true)
+ }
+
+ @Test
+ fun onBeforeRenderList_deviceLocked_notifNeedsRedaction() {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener = withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
+
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false)
+ whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(false)
+ val entry = fakeNotification(1, true)
+
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+
+ verify(entry.representativeEntry!!).setSensitive(true, true)
+ }
+
+ @Test
+ fun onBeforeRenderList_deviceDynamicallyUnlocked_notifNeedsRedaction() {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener = withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
+
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false)
+ whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(true)
+ val entry = fakeNotification(1, true)
+
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+
+ verify(entry.representativeEntry!!).setSensitive(false, true)
+ }
+
+ @Test
+ fun onBeforeRenderList_deviceDynamicallyUnlocked_notifUserNeedsWorkChallenge() {
+ coordinator.attach(pipeline)
+ val onBeforeRenderListListener = withArgCaptor<OnBeforeRenderListListener> {
+ verify(pipeline).addOnBeforeRenderListListener(capture())
+ }
+
+ whenever(lockscreenUserManager.currentUserId).thenReturn(1)
+ whenever(lockscreenUserManager.isLockscreenPublicMode(1)).thenReturn(true)
+ whenever(lockscreenUserManager.userAllowsPrivateNotificationsInPublic(1)).thenReturn(false)
+ whenever(dynamicPrivacyController.isDynamicallyUnlocked).thenReturn(true)
+ whenever(lockscreenUserManager.needsSeparateWorkChallenge(2)).thenReturn(true)
+
+ val entry = fakeNotification(2, true)
+
+ onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
+
+ verify(entry.representativeEntry!!).setSensitive(true, true)
+ }
+
+ private fun fakeNotification(notifUserId: Int, needsRedaction: Boolean): ListEntry {
+ val mockUserHandle = mock<UserHandle>().apply {
+ whenever(identifier).thenReturn(notifUserId)
+ }
+ val mockSbn: StatusBarNotification = mock<StatusBarNotification>().apply {
+ whenever(user).thenReturn(mockUserHandle)
+ }
+ val mockEntry = mock<NotificationEntry>().apply {
+ whenever(sbn).thenReturn(mockSbn)
+ }
+ whenever(lockscreenUserManager.needsRedaction(mockEntry)).thenReturn(needsRedaction)
+ whenever(mockEntry.rowExists()).thenReturn(true)
+ return object : ListEntry("key", 0) {
+ override fun getRepresentativeEntry(): NotificationEntry = mockEntry
+ }
+ }
+} \ No newline at end of file