Add a debug mode that allows disabling notifications for all packages that aren't allowed.
Bug: 209080636 Test: manual Change-Id: Ifc9e348c5d4c8075a62ac95bd88596dbb33b22e1
This commit is contained in:
@@ -34,6 +34,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
|
||||
import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||
import com.android.systemui.statusbar.notification.collection.provider.DebugModeFilterProvider;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@@ -44,6 +45,7 @@ import javax.inject.Inject;
|
||||
@SysUISingleton
|
||||
public class NotificationFilter {
|
||||
|
||||
private final DebugModeFilterProvider mDebugNotificationFilter;
|
||||
private final StatusBarStateController mStatusBarStateController;
|
||||
private final KeyguardEnvironment mKeyguardEnvironment;
|
||||
private final ForegroundServiceController mForegroundServiceController;
|
||||
@@ -52,11 +54,13 @@ public class NotificationFilter {
|
||||
|
||||
@Inject
|
||||
public NotificationFilter(
|
||||
DebugModeFilterProvider debugNotificationFilter,
|
||||
StatusBarStateController statusBarStateController,
|
||||
KeyguardEnvironment keyguardEnvironment,
|
||||
ForegroundServiceController foregroundServiceController,
|
||||
NotificationLockscreenUserManager userManager,
|
||||
MediaFeatureFlag mediaFeatureFlag) {
|
||||
mDebugNotificationFilter = debugNotificationFilter;
|
||||
mStatusBarStateController = statusBarStateController;
|
||||
mKeyguardEnvironment = keyguardEnvironment;
|
||||
mForegroundServiceController = foregroundServiceController;
|
||||
@@ -69,6 +73,10 @@ public class NotificationFilter {
|
||||
*/
|
||||
public boolean shouldFilterOut(NotificationEntry entry) {
|
||||
final StatusBarNotification sbn = entry.getSbn();
|
||||
if (mDebugNotificationFilter.shouldFilterOut(entry)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(mKeyguardEnvironment.isDeviceProvisioned()
|
||||
|| showNotificationEvenIfUnprovisioned(sbn))) {
|
||||
return true;
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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 com.android.systemui.statusbar.notification.collection.NotifPipeline
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry
|
||||
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
|
||||
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter
|
||||
import com.android.systemui.statusbar.notification.collection.provider.DebugModeFilterProvider
|
||||
import javax.inject.Inject
|
||||
|
||||
/** A small coordinator which filters out notifications from non-allowed apps. */
|
||||
@CoordinatorScope
|
||||
class DebugModeCoordinator @Inject constructor(
|
||||
private val debugModeFilterProvider: DebugModeFilterProvider
|
||||
) : Coordinator {
|
||||
|
||||
override fun attach(pipeline: NotifPipeline) {
|
||||
pipeline.addPreGroupFilter(preGroupFilter)
|
||||
debugModeFilterProvider.registerInvalidationListener(preGroupFilter::invalidateList)
|
||||
}
|
||||
|
||||
private val preGroupFilter = object : NotifFilter("DebugModeCoordinator") {
|
||||
override fun shouldFilterOut(entry: NotificationEntry, now: Long) =
|
||||
debugModeFilterProvider.shouldFilterOut(entry)
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,7 @@ class NotifCoordinatorsImpl @Inject constructor(
|
||||
gutsCoordinator: GutsCoordinator,
|
||||
communalCoordinator: CommunalCoordinator,
|
||||
conversationCoordinator: ConversationCoordinator,
|
||||
debugModeCoordinator: DebugModeCoordinator,
|
||||
groupCountCoordinator: GroupCountCoordinator,
|
||||
mediaCoordinator: MediaCoordinator,
|
||||
preparationCoordinator: PreparationCoordinator,
|
||||
@@ -86,6 +87,7 @@ class NotifCoordinatorsImpl @Inject constructor(
|
||||
mCoordinators.add(deviceProvisionedCoordinator)
|
||||
mCoordinators.add(bubbleCoordinator)
|
||||
mCoordinators.add(communalCoordinator)
|
||||
mCoordinators.add(debugModeCoordinator)
|
||||
mCoordinators.add(conversationCoordinator)
|
||||
mCoordinators.add(groupCountCoordinator)
|
||||
mCoordinators.add(mediaCoordinator)
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* 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.provider
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import com.android.systemui.Dumpable
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dump.DumpManager
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry
|
||||
import com.android.systemui.util.Assert
|
||||
import com.android.systemui.util.ListenerSet
|
||||
import com.android.systemui.util.isNotEmpty
|
||||
import java.io.FileDescriptor
|
||||
import java.io.PrintWriter
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* A debug mode provider which is used by both the legacy and new notification pipelines to
|
||||
* block unwanted notifications from appearing to the user, primarily for integration testing.
|
||||
*
|
||||
* The only configuration is a list of allowed packages. When this list is empty, the feature is
|
||||
* disabled. When SystemUI starts up, this feature is disabled.
|
||||
*
|
||||
* To enabled filtering, provide the list of packages in a comma-separated list using the command:
|
||||
*
|
||||
* `$ adb shell am broadcast -a com.android.systemui.action.SET_NOTIF_DEBUG_MODE
|
||||
* --esal allowed_packages <comma-separated-packages>`
|
||||
*
|
||||
* To disable filtering, send the action without a list:
|
||||
*
|
||||
* `$ adb shell am broadcast -a com.android.systemui.action.SET_NOTIF_DEBUG_MODE`
|
||||
*
|
||||
* NOTE: this feature only works on debug builds, and when the broadcaster is root.
|
||||
*/
|
||||
@SysUISingleton
|
||||
class DebugModeFilterProvider @Inject constructor(
|
||||
private val context: Context,
|
||||
dumpManager: DumpManager
|
||||
) : Dumpable {
|
||||
private var allowedPackages: List<String> = emptyList()
|
||||
private val listeners = ListenerSet<Runnable>()
|
||||
|
||||
init {
|
||||
dumpManager.registerDumpable(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a runnable to be invoked when the allowed packages changes, which would mean the
|
||||
* result of [shouldFilterOut] may have changed for some entries.
|
||||
*/
|
||||
fun registerInvalidationListener(listener: Runnable) {
|
||||
Assert.isMainThread()
|
||||
if (!Build.isDebuggable()) {
|
||||
return
|
||||
}
|
||||
val needsInitialization = listeners.isEmpty()
|
||||
listeners.addIfAbsent(listener)
|
||||
if (needsInitialization) {
|
||||
val filter = IntentFilter().apply { addAction(ACTION_SET_NOTIF_DEBUG_MODE) }
|
||||
val permission = NOTIF_DEBUG_MODE_PERMISSION
|
||||
context.registerReceiver(mReceiver, filter, permission, null)
|
||||
Log.d(TAG, "Registered: $mReceiver")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given entry should be hidden from the user in debug mode.
|
||||
* Will always return false in release.
|
||||
*/
|
||||
fun shouldFilterOut(entry: NotificationEntry): Boolean {
|
||||
if (allowedPackages.isEmpty()) {
|
||||
return false
|
||||
}
|
||||
return entry.sbn.packageName !in allowedPackages
|
||||
}
|
||||
|
||||
override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
|
||||
pw.println("initialized: ${listeners.isNotEmpty()}")
|
||||
pw.println("allowedPackages: ${allowedPackages.size}")
|
||||
allowedPackages.forEachIndexed { i, pkg ->
|
||||
pw.println(" [$i]: $pkg")
|
||||
}
|
||||
}
|
||||
|
||||
private val mReceiver: BroadcastReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent?) {
|
||||
val action = intent?.action
|
||||
if (ACTION_SET_NOTIF_DEBUG_MODE == action) {
|
||||
allowedPackages = intent.extras?.getStringArrayList(EXTRA_ALLOWED_PACKAGES)
|
||||
?: emptyList()
|
||||
Log.d(TAG, "Updated allowedPackages: $allowedPackages")
|
||||
listeners.forEach(Runnable::run)
|
||||
} else {
|
||||
Log.d(TAG, "Malformed intent: $intent")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "DebugModeFilterProvider"
|
||||
private const val ACTION_SET_NOTIF_DEBUG_MODE =
|
||||
"com.android.systemui.action.SET_NOTIF_DEBUG_MODE"
|
||||
private const val NOTIF_DEBUG_MODE_PERMISSION =
|
||||
"com.android.systemui.permission.NOTIF_DEBUG_MODE"
|
||||
private const val EXTRA_ALLOWED_PACKAGES = "allowed_packages"
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,7 @@ import com.android.systemui.statusbar.notification.collection.inflation.Notifica
|
||||
import com.android.systemui.statusbar.notification.collection.init.NotifPipelineInitializer
|
||||
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
|
||||
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
|
||||
import com.android.systemui.statusbar.notification.collection.provider.DebugModeFilterProvider
|
||||
import com.android.systemui.statusbar.notification.collection.render.NotifStackController
|
||||
import com.android.systemui.statusbar.notification.interruption.HeadsUpController
|
||||
import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder
|
||||
@@ -65,6 +66,7 @@ class NotificationsControllerImpl @Inject constructor(
|
||||
private val notifPipelineFlags: NotifPipelineFlags,
|
||||
private val notificationListener: NotificationListener,
|
||||
private val entryManager: NotificationEntryManager,
|
||||
private val debugModeFilterProvider: DebugModeFilterProvider,
|
||||
private val legacyRanker: NotificationRankingManager,
|
||||
private val commonNotifCollection: Lazy<CommonNotifCollection>,
|
||||
private val notifPipeline: Lazy<NotifPipeline>,
|
||||
@@ -134,6 +136,9 @@ class NotificationsControllerImpl @Inject constructor(
|
||||
headsUpController.attach(entryManager, headsUpManager)
|
||||
groupManagerLegacy.get().setHeadsUpManager(headsUpManager)
|
||||
groupAlertTransferHelper.setHeadsUpManager(headsUpManager)
|
||||
debugModeFilterProvider.registerInvalidationListener {
|
||||
entryManager.updateNotifications("debug mode filter changed")
|
||||
}
|
||||
|
||||
entryManager.initialize(notificationListener, legacyRanker)
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
import android.testing.TestableLooper.RunWithLooper;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.test.annotation.UiThreadTest;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
@@ -50,6 +51,7 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager.Keyg
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
|
||||
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
|
||||
import com.android.systemui.statusbar.notification.collection.provider.DebugModeFilterProvider;
|
||||
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
|
||||
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
|
||||
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
|
||||
@@ -77,6 +79,8 @@ public class NotificationFilterTest extends SysuiTestCase {
|
||||
private final StatusBarNotification mMockStatusBarNotification =
|
||||
mock(StatusBarNotification.class);
|
||||
|
||||
@Mock
|
||||
DebugModeFilterProvider mDebugModeFilterProvider;
|
||||
@Mock
|
||||
StatusBarStateController mStatusBarStateController;
|
||||
@Mock
|
||||
@@ -132,7 +136,13 @@ public class NotificationFilterTest extends SysuiTestCase {
|
||||
mDependency,
|
||||
TestableLooper.get(this));
|
||||
mRow = testHelper.createRow();
|
||||
mNotificationFilter = new NotificationFilter(
|
||||
mNotificationFilter = newNotificationFilter();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private NotificationFilter newNotificationFilter() {
|
||||
return new NotificationFilter(
|
||||
mDebugModeFilterProvider,
|
||||
mStatusBarStateController,
|
||||
mEnvironment,
|
||||
mFsc,
|
||||
@@ -205,12 +215,7 @@ public class NotificationFilterTest extends SysuiTestCase {
|
||||
public void shouldFilterOtherNotificationWhenDisabled() {
|
||||
// GIVEN that the media feature is disabled
|
||||
when(mMediaFeatureFlag.getEnabled()).thenReturn(false);
|
||||
NotificationFilter filter = new NotificationFilter(
|
||||
mStatusBarStateController,
|
||||
mEnvironment,
|
||||
mFsc,
|
||||
mUserManager,
|
||||
mMediaFeatureFlag);
|
||||
NotificationFilter filter = newNotificationFilter();
|
||||
// WHEN the media filter is asked about an entry
|
||||
NotificationEntry otherEntry = new NotificationEntryBuilder().build();
|
||||
final boolean shouldFilter = filter.shouldFilterOut(otherEntry);
|
||||
@@ -222,12 +227,7 @@ public class NotificationFilterTest extends SysuiTestCase {
|
||||
public void shouldFilterOtherNotificationWhenEnabled() {
|
||||
// GIVEN that the media feature is enabled
|
||||
when(mMediaFeatureFlag.getEnabled()).thenReturn(true);
|
||||
NotificationFilter filter = new NotificationFilter(
|
||||
mStatusBarStateController,
|
||||
mEnvironment,
|
||||
mFsc,
|
||||
mUserManager,
|
||||
mMediaFeatureFlag);
|
||||
NotificationFilter filter = newNotificationFilter();
|
||||
// WHEN the media filter is asked about an entry
|
||||
NotificationEntry otherEntry = new NotificationEntryBuilder().build();
|
||||
final boolean shouldFilter = filter.shouldFilterOut(otherEntry);
|
||||
@@ -239,12 +239,7 @@ public class NotificationFilterTest extends SysuiTestCase {
|
||||
public void shouldFilterMediaNotificationWhenDisabled() {
|
||||
// GIVEN that the media feature is disabled
|
||||
when(mMediaFeatureFlag.getEnabled()).thenReturn(false);
|
||||
NotificationFilter filter = new NotificationFilter(
|
||||
mStatusBarStateController,
|
||||
mEnvironment,
|
||||
mFsc,
|
||||
mUserManager,
|
||||
mMediaFeatureFlag);
|
||||
NotificationFilter filter = newNotificationFilter();
|
||||
// WHEN the media filter is asked about a media entry
|
||||
final boolean shouldFilter = filter.shouldFilterOut(mMediaEntry);
|
||||
// THEN it shouldn't be filtered
|
||||
@@ -255,12 +250,7 @@ public class NotificationFilterTest extends SysuiTestCase {
|
||||
public void shouldFilterMediaNotificationWhenEnabled() {
|
||||
// GIVEN that the media feature is enabled
|
||||
when(mMediaFeatureFlag.getEnabled()).thenReturn(true);
|
||||
NotificationFilter filter = new NotificationFilter(
|
||||
mStatusBarStateController,
|
||||
mEnvironment,
|
||||
mFsc,
|
||||
mUserManager,
|
||||
mMediaFeatureFlag);
|
||||
NotificationFilter filter = newNotificationFilter();
|
||||
// WHEN the media filter is asked about a media entry
|
||||
final boolean shouldFilter = filter.shouldFilterOut(mMediaEntry);
|
||||
// THEN it should be filtered
|
||||
|
||||
Reference in New Issue
Block a user