1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.statusbar.notification.collection.provider 18 19 import android.os.Build 20 import android.util.Log 21 import com.android.systemui.Dumpable 22 import com.android.systemui.dagger.SysUISingleton 23 import com.android.systemui.dump.DumpManager 24 import com.android.systemui.statusbar.commandline.Command 25 import com.android.systemui.statusbar.commandline.CommandRegistry 26 import com.android.systemui.statusbar.notification.collection.NotificationEntry 27 import com.android.systemui.util.Assert 28 import com.android.systemui.util.ListenerSet 29 import com.android.systemui.util.asIndenting 30 import com.android.systemui.util.printCollection 31 import com.android.systemui.util.println 32 import java.io.PrintWriter 33 import javax.inject.Inject 34 35 /** 36 * A debug mode provider which is used by both the legacy and new notification pipelines to 37 * block unwanted notifications from appearing to the user, primarily for integration testing. 38 * 39 * The only configuration is a list of allowed packages. When this list is empty, the feature is 40 * disabled. When SystemUI starts up, this feature is disabled. 41 * 42 * To enabled filtering, provide the space-separated list of packages using the command: 43 * 44 * `$ adb shell cmd statusbar notif-filter allowed-pkgs <package> ...` 45 * 46 * To disable filtering, send the command without any packages, or explicitly reset: 47 * 48 * `$ adb shell cmd statusbar notif-filter reset` 49 * 50 * NOTE: this feature only works on debug builds, and when the broadcaster is root. 51 */ 52 @SysUISingleton 53 class DebugModeFilterProvider @Inject constructor( 54 private val commandRegistry: CommandRegistry, 55 dumpManager: DumpManager 56 ) : Dumpable { 57 private var allowedPackages: List<String> = emptyList() 58 private val listeners = ListenerSet<Runnable>() 59 60 init { 61 dumpManager.registerDumpable(this) 62 } 63 64 /** 65 * Register a runnable to be invoked when the allowed packages changes, which would mean the 66 * result of [shouldFilterOut] may have changed for some entries. 67 */ registerInvalidationListenernull68 fun registerInvalidationListener(listener: Runnable) { 69 Assert.isMainThread() 70 if (!Build.isDebuggable()) { 71 return 72 } 73 val needsInitialization = listeners.isEmpty() 74 listeners.addIfAbsent(listener) 75 if (needsInitialization) { 76 commandRegistry.registerCommand("notif-filter") { NotifFilterCommand() } 77 Log.d(TAG, "Registered notif-filter command") 78 } 79 } 80 81 /** 82 * Determine if the given entry should be hidden from the user in debug mode. 83 * Will always return false in release. 84 */ shouldFilterOutnull85 fun shouldFilterOut(entry: NotificationEntry): Boolean { 86 if (allowedPackages.isEmpty()) { 87 return false 88 } 89 return entry.sbn.packageName !in allowedPackages 90 } 91 <lambda>null92 override fun dump(pw: PrintWriter, args: Array<out String>) = pw.asIndenting().run { 93 println("initialized", listeners.isNotEmpty()) 94 printCollection("allowedPackages", allowedPackages) 95 } 96 97 companion object { 98 private const val TAG = "DebugModeFilterProvider" 99 } 100 101 inner class NotifFilterCommand : Command { executenull102 override fun execute(pw: PrintWriter, args: List<String>) { 103 when (args.firstOrNull()) { 104 "reset" -> { 105 if (args.size > 1) { 106 return invalidCommand(pw, "Unexpected arguments for 'reset' command") 107 } 108 allowedPackages = emptyList() 109 } 110 "allowed-pkgs" -> { 111 allowedPackages = args.drop(1) 112 } 113 null -> return invalidCommand(pw, "Missing command") 114 else -> return invalidCommand(pw, "Unknown command: ${args.firstOrNull()}") 115 } 116 Log.d(TAG, "Updated allowedPackages: $allowedPackages") 117 if (allowedPackages.isEmpty()) { 118 pw.print("Resetting allowedPackages ... ") 119 } else { 120 pw.print("Updating allowedPackages: $allowedPackages ... ") 121 } 122 listeners.forEach(Runnable::run) 123 pw.println("DONE") 124 } 125 invalidCommandnull126 private fun invalidCommand(pw: PrintWriter, reason: String) { 127 pw.println("Error: $reason") 128 pw.println() 129 help(pw) 130 } 131 helpnull132 override fun help(pw: PrintWriter) { 133 pw.println("Usage: adb shell cmd statusbar notif-filter <command>") 134 pw.println("Available commands:") 135 pw.println(" reset") 136 pw.println(" Restore the default system behavior.") 137 pw.println(" allowed-pkgs <package> ...") 138 pw.println(" Hide all notification except from packages listed here.") 139 pw.println(" Providing no packages is treated as a reset.") 140 } 141 } 142 } 143