1 /*
2  * Copyright (C) 2019 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.broadcast
18 
19 import android.content.BroadcastReceiver
20 import android.content.Context
21 import android.content.Intent
22 import android.content.IntentFilter
23 import android.os.Handler
24 import android.os.HandlerExecutor
25 import android.os.Looper
26 import android.os.Message
27 import android.os.UserHandle
28 import android.text.TextUtils
29 import android.util.IndentingPrintWriter
30 import android.util.SparseArray
31 import com.android.internal.annotations.VisibleForTesting
32 import com.android.systemui.Dumpable
33 import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger
34 import com.android.systemui.dump.DumpManager
35 import com.android.systemui.settings.UserTracker
36 import java.io.FileDescriptor
37 import java.io.PrintWriter
38 import java.util.concurrent.Executor
39 
40 data class ReceiverData(
41     val receiver: BroadcastReceiver,
42     val filter: IntentFilter,
43     val executor: Executor,
44     val user: UserHandle
45 )
46 
47 private const val MSG_ADD_RECEIVER = 0
48 private const val MSG_REMOVE_RECEIVER = 1
49 private const val MSG_REMOVE_RECEIVER_FOR_USER = 2
50 private const val TAG = "BroadcastDispatcher"
51 private const val DEBUG = true
52 
53 /**
54  * SystemUI master Broadcast Dispatcher.
55  *
56  * This class allows [BroadcastReceiver] to register and centralizes registrations to [Context]
57  * from SystemUI. That way the number of calls to [BroadcastReceiver.onReceive] can be reduced for
58  * a given broadcast.
59  *
60  * Use only for IntentFilters with actions and optionally categories. It does not support,
61  * permissions, schemes, data types, data authorities or priority different than 0.
62  * Cannot be used for getting sticky broadcasts (either as return of registering or as re-delivery).
63  */
64 open class BroadcastDispatcher constructor (
65     private val context: Context,
66     private val bgLooper: Looper,
67     private val bgExecutor: Executor,
68     private val dumpManager: DumpManager,
69     private val logger: BroadcastDispatcherLogger,
70     private val userTracker: UserTracker
71 ) : Dumpable {
72 
73     // Only modify in BG thread
74     private val receiversByUser = SparseArray<UserBroadcastDispatcher>(20)
75 
76     fun initialize() {
77         dumpManager.registerDumpable(javaClass.name, this)
78     }
79 
80     /**
81      * Register a receiver for broadcast with the dispatcher
82      *
83      * @param receiver A receiver to dispatch the [Intent]
84      * @param filter A filter to determine what broadcasts should be dispatched to this receiver.
85      *               It will only take into account actions and categories for filtering. It must
86      *               have at least one action.
87      * @param handler A handler to dispatch [BroadcastReceiver.onReceive].
88      * @param user A user handle to determine which broadcast should be dispatched to this receiver.
89      *             By default, it is the user of the context (system user in SystemUI).
90      * @throws IllegalArgumentException if the filter has other constraints that are not actions or
91      *                                  categories or the filter has no actions.
92      */
93     @Deprecated(message = "Replacing Handler for Executor in SystemUI",
94             replaceWith = ReplaceWith("registerReceiver(receiver, filter, executor, user)"))
95     @JvmOverloads
96     open fun registerReceiverWithHandler(
97         receiver: BroadcastReceiver,
98         filter: IntentFilter,
99         handler: Handler,
100         user: UserHandle = context.user
101     ) {
102         registerReceiver(receiver, filter, HandlerExecutor(handler), user)
103     }
104 
105     /**
106      * Register a receiver for broadcast with the dispatcher
107      *
108      * @param receiver A receiver to dispatch the [Intent]
109      * @param filter A filter to determine what broadcasts should be dispatched to this receiver.
110      *               It will only take into account actions and categories for filtering. It must
111      *               have at least one action.
112      * @param executor An executor to dispatch [BroadcastReceiver.onReceive]. Pass null to use an
113      *                 executor in the main thread (default).
114      * @param user A user handle to determine which broadcast should be dispatched to this receiver.
115      *             Pass `null` to use the user of the context (system user in SystemUI).
116      * @throws IllegalArgumentException if the filter has other constraints that are not actions or
117      *                                  categories or the filter has no actions.
118      */
119     @JvmOverloads
120     open fun registerReceiver(
121         receiver: BroadcastReceiver,
122         filter: IntentFilter,
123         executor: Executor? = null,
124         user: UserHandle? = null
125     ) {
126         checkFilter(filter)
127         this.handler
128                 .obtainMessage(MSG_ADD_RECEIVER, ReceiverData(
129                         receiver,
130                         filter,
131                         executor ?: context.mainExecutor,
132                         user ?: context.user
133                 ))
134                 .sendToTarget()
135     }
136 
137     private fun checkFilter(filter: IntentFilter) {
138         val sb = StringBuilder()
139         if (filter.countActions() == 0) sb.append("Filter must contain at least one action. ")
140         if (filter.countDataAuthorities() != 0) sb.append("Filter cannot contain DataAuthorities. ")
141         if (filter.countDataPaths() != 0) sb.append("Filter cannot contain DataPaths. ")
142         if (filter.countDataSchemes() != 0) sb.append("Filter cannot contain DataSchemes. ")
143         if (filter.countDataTypes() != 0) sb.append("Filter cannot contain DataTypes. ")
144         if (filter.priority != 0) sb.append("Filter cannot modify priority. ")
145         if (!TextUtils.isEmpty(sb)) throw IllegalArgumentException(sb.toString())
146     }
147 
148     /**
149      * Unregister receiver for all users.
150      * <br>
151      * This will remove every registration of [receiver], not those done just with [UserHandle.ALL].
152      *
153      * @param receiver The receiver to unregister. It will be unregistered for all users.
154      */
155     open fun unregisterReceiver(receiver: BroadcastReceiver) {
156         handler.obtainMessage(MSG_REMOVE_RECEIVER, receiver).sendToTarget()
157     }
158 
159     /**
160      * Unregister receiver for a particular user.
161      *
162      * @param receiver The receiver to unregister.
163      * @param user The user associated to the registered [receiver]. It can be [UserHandle.ALL].
164      */
165     open fun unregisterReceiverForUser(receiver: BroadcastReceiver, user: UserHandle) {
166         handler.obtainMessage(MSG_REMOVE_RECEIVER_FOR_USER, user.identifier, 0, receiver)
167                 .sendToTarget()
168     }
169 
170     @VisibleForTesting
171     protected open fun createUBRForUser(userId: Int) =
172             UserBroadcastDispatcher(context, userId, bgLooper, bgExecutor, logger)
173 
174     override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
175         pw.println("Broadcast dispatcher:")
176         val ipw = IndentingPrintWriter(pw, "  ")
177         ipw.increaseIndent()
178         for (index in 0 until receiversByUser.size()) {
179             ipw.println("User ${receiversByUser.keyAt(index)}")
180             receiversByUser.valueAt(index).dump(fd, ipw, args)
181         }
182         ipw.decreaseIndent()
183     }
184 
185     private val handler = object : Handler(bgLooper) {
186 
187         override fun handleMessage(msg: Message) {
188             when (msg.what) {
189                 MSG_ADD_RECEIVER -> {
190                     val data = msg.obj as ReceiverData
191                     // If the receiver asked to be registered under the current user, we register
192                     // under the actual current user.
193                     val userId = if (data.user.identifier == UserHandle.USER_CURRENT) {
194                         userTracker.userId
195                     } else {
196                         data.user.identifier
197                     }
198                     if (userId < UserHandle.USER_ALL) {
199                         throw IllegalStateException(
200                                 "Attempting to register receiver for invalid user {$userId}")
201                     }
202                     val uBR = receiversByUser.get(userId, createUBRForUser(userId))
203                     receiversByUser.put(userId, uBR)
204                     uBR.registerReceiver(data)
205                 }
206 
207                 MSG_REMOVE_RECEIVER -> {
208                     for (it in 0 until receiversByUser.size()) {
209                         receiversByUser.valueAt(it).unregisterReceiver(msg.obj as BroadcastReceiver)
210                     }
211                 }
212 
213                 MSG_REMOVE_RECEIVER_FOR_USER -> {
214                     receiversByUser.get(msg.arg1)?.unregisterReceiver(msg.obj as BroadcastReceiver)
215                 }
216                 else -> super.handleMessage(msg)
217             }
218         }
219     }
220 }
221