• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * 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.isNotEmpty
30 import java.io.PrintWriter
31 import javax.inject.Inject
32 
33 /**
34  * A debug mode provider which is used by both the legacy and new notification pipelines to
35  * block unwanted notifications from appearing to the user, primarily for integration testing.
36  *
37  * The only configuration is a list of allowed packages.  When this list is empty, the feature is
38  * disabled.  When SystemUI starts up, this feature is disabled.
39  *
40  * To enabled filtering, provide the space-separated list of packages using the command:
41  *
42  * `$ adb shell cmd statusbar notif-filter allowed-pkgs <package> ...`
43  *
44  * To disable filtering, send the command without any packages, or explicitly reset:
45  *
46  * `$ adb shell cmd statusbar notif-filter reset`
47  *
48  * NOTE: this feature only works on debug builds, and when the broadcaster is root.
49  */
50 @SysUISingleton
51 class DebugModeFilterProvider @Inject constructor(
52     private val commandRegistry: CommandRegistry,
53     dumpManager: DumpManager
54 ) : Dumpable {
55     private var allowedPackages: List<String> = emptyList()
56     private val listeners = ListenerSet<Runnable>()
57 
58     init {
59         dumpManager.registerDumpable(this)
60     }
61 
62     /**
63      * Register a runnable to be invoked when the allowed packages changes, which would mean the
64      * result of [shouldFilterOut] may have changed for some entries.
65      */
66     fun registerInvalidationListener(listener: Runnable) {
67         Assert.isMainThread()
68         if (!Build.isDebuggable()) {
69             return
70         }
71         val needsInitialization = listeners.isEmpty()
72         listeners.addIfAbsent(listener)
73         if (needsInitialization) {
74             commandRegistry.registerCommand("notif-filter") { NotifFilterCommand() }
75             Log.d(TAG, "Registered notif-filter command")
76         }
77     }
78 
79     /**
80      * Determine if the given entry should be hidden from the user in debug mode.
81      * Will always return false in release.
82      */
83     fun shouldFilterOut(entry: NotificationEntry): Boolean {
84         if (allowedPackages.isEmpty()) {
85             return false
86         }
87         return entry.sbn.packageName !in allowedPackages
88     }
89 
90     override fun dump(pw: PrintWriter, args: Array<out String>) {
91         pw.println("initialized: ${listeners.isNotEmpty()}")
92         pw.println("allowedPackages: ${allowedPackages.size}")
93         allowedPackages.forEachIndexed { i, pkg ->
94             pw.println("  [$i]: $pkg")
95         }
96     }
97 
98     companion object {
99         private const val TAG = "DebugModeFilterProvider"
100     }
101 
102     inner class NotifFilterCommand : Command {
103         override fun execute(pw: PrintWriter, args: List<String>) {
104             when (args.firstOrNull()) {
105                 "reset" -> {
106                     if (args.size > 1) {
107                         return invalidCommand(pw, "Unexpected arguments for 'reset' command")
108                     }
109                     allowedPackages = emptyList()
110                 }
111                 "allowed-pkgs" -> {
112                     allowedPackages = args.drop(1)
113                 }
114                 null -> return invalidCommand(pw, "Missing command")
115                 else -> return invalidCommand(pw, "Unknown command: ${args.firstOrNull()}")
116             }
117             Log.d(TAG, "Updated allowedPackages: $allowedPackages")
118             if (allowedPackages.isEmpty()) {
119                 pw.print("Resetting allowedPackages ... ")
120             } else {
121                 pw.print("Updating allowedPackages: $allowedPackages ... ")
122             }
123             listeners.forEach(Runnable::run)
124             pw.println("DONE")
125         }
126 
127         private fun invalidCommand(pw: PrintWriter, reason: String) {
128             pw.println("Error: $reason")
129             pw.println()
130             help(pw)
131         }
132 
133         override fun help(pw: PrintWriter) {
134             pw.println("Usage: adb shell cmd statusbar notif-filter <command>")
135             pw.println("Available commands:")
136             pw.println("  reset")
137             pw.println("     Restore the default system behavior.")
138             pw.println("  allowed-pkgs <package> ...")
139             pw.println("     Hide all notification except from packages listed here.")
140             pw.println("     Providing no packages is treated as a reset.")
141         }
142     }
143 }
144