1 /*
<lambda>null2  * 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.annotation.SuppressLint
20 import android.content.BroadcastReceiver
21 import android.content.Context
22 import android.os.Handler
23 import android.os.Looper
24 import android.os.Trace
25 import android.os.UserHandle
26 import android.util.ArrayMap
27 import android.util.ArraySet
28 import android.util.Log
29 import androidx.annotation.VisibleForTesting
30 import androidx.annotation.WorkerThread
31 import com.android.internal.util.Preconditions
32 import com.android.systemui.Dumpable
33 import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger
34 import com.android.systemui.util.indentIfPossible
35 import java.io.PrintWriter
36 import java.util.concurrent.Executor
37 import java.util.concurrent.atomic.AtomicInteger
38 
39 private const val TAG = "UserBroadcastDispatcher"
40 private const val DEBUG = false
41 
42 /**
43  * Broadcast dispatcher for a given user registration [userId].
44  *
45  * Created by [BroadcastDispatcher] as needed by users. The value of [userId] can be
46  * [UserHandle.USER_ALL].
47  */
48 open class UserBroadcastDispatcher(
49     private val context: Context,
50     private val userId: Int,
51     private val workerLooper: Looper,
52     private val workerExecutor: Executor,
53     private val logger: BroadcastDispatcherLogger,
54     private val removalPendingStore: PendingRemovalStore
55 ) : Dumpable {
56 
57     companion object {
58         // Used only for debugging. If not debugging, this variable will not be accessed and all
59         // received broadcasts will be tagged with 0. However, as DEBUG is false, nothing will be
60         // logged
61         val index = AtomicInteger(0)
62     }
63 
64     // Used for key in actionsToActionsReceivers
65     internal data class ReceiverProperties(
66         val action: String,
67         val flags: Int,
68         val permission: String?
69     )
70 
71     private val wrongThreadErrorMsg = "This method should only be called from the worker thread " +
72             "(which is expected to be the BroadcastRunning thread)"
73     private val workerHandler = Handler(workerLooper)
74 
75     // Only modify in BroadcastRunning thread
76     @VisibleForTesting
77     internal val actionsToActionsReceivers = ArrayMap<ReceiverProperties, ActionReceiver>()
78     private val receiverToActions = ArrayMap<BroadcastReceiver, MutableSet<String>>()
79 
80     @VisibleForTesting
81     internal fun isReceiverReferenceHeld(receiver: BroadcastReceiver): Boolean {
82         return actionsToActionsReceivers.values.any {
83             it.hasReceiver(receiver)
84         } || (receiver in receiverToActions)
85     }
86 
87     /**
88      * Register a [ReceiverData] for this user.
89      */
90     @WorkerThread
91     fun registerReceiver(receiverData: ReceiverData, flags: Int) {
92         handleRegisterReceiver(receiverData, flags)
93     }
94 
95     /**
96      * Unregister a given [BroadcastReceiver] for this user.
97      */
98     @WorkerThread
99     fun unregisterReceiver(receiver: BroadcastReceiver) {
100         handleUnregisterReceiver(receiver)
101     }
102 
103     private fun handleRegisterReceiver(receiverData: ReceiverData, flags: Int) {
104         Preconditions.checkState(workerLooper.isCurrentThread, wrongThreadErrorMsg)
105         if (DEBUG) Log.w(TAG, "Register receiver: ${receiverData.receiver}")
106         receiverToActions
107                 .getOrPut(receiverData.receiver, { ArraySet() })
108                 .addAll(receiverData.filter.actionsIterator()?.asSequence() ?: emptySequence())
109         receiverData.filter.actionsIterator().forEach {
110             actionsToActionsReceivers
111                 .getOrPut(
112                     ReceiverProperties(it, flags, receiverData.permission),
113                     { createActionReceiver(it, receiverData.permission, flags) })
114                 .addReceiverData(receiverData)
115         }
116         logger.logReceiverRegistered(userId, receiverData.receiver, flags)
117     }
118 
119     @SuppressLint("RegisterReceiverViaContextDetector")
120     @VisibleForTesting
121     internal open fun createActionReceiver(
122         action: String,
123         permission: String?,
124         flags: Int
125     ): ActionReceiver {
126         return ActionReceiver(
127                 action,
128                 userId,
129                 {
130                     if (Trace.isEnabled()) {
131                         Trace.traceBegin(
132                                 Trace.TRACE_TAG_APP, "registerReceiver act=$action user=$userId")
133                     }
134                     context.registerReceiverAsUser(
135                             this,
136                             UserHandle.of(userId),
137                             it,
138                             permission,
139                             workerHandler,
140                             flags
141                     )
142                     Trace.endSection()
143                     logger.logContextReceiverRegistered(userId, flags, it)
144                 },
145                 {
146                     try {
147                         if (Trace.isEnabled()) {
148                             Trace.traceBegin(
149                                     Trace.TRACE_TAG_APP,
150                                     "unregisterReceiver act=$action user=$userId")
151                         }
152                         context.unregisterReceiver(this)
153                         Trace.endSection()
154                         logger.logContextReceiverUnregistered(userId, action)
155                     } catch (e: IllegalArgumentException) {
156                         Log.e(TAG, "Trying to unregister unregistered receiver for user $userId, " +
157                                 "action $action",
158                                 IllegalStateException(e))
159                     }
160                 },
161                 workerExecutor,
162                 logger,
163                 removalPendingStore::isPendingRemoval
164         )
165     }
166 
167     private fun handleUnregisterReceiver(receiver: BroadcastReceiver) {
168         Preconditions.checkState(workerLooper.isCurrentThread, wrongThreadErrorMsg)
169         if (DEBUG) Log.w(TAG, "Unregister receiver: $receiver")
170         receiverToActions.getOrDefault(receiver, mutableSetOf()).forEach {
171             actionsToActionsReceivers.forEach { (key, value) ->
172                 if (key.action == it) {
173                     value.removeReceiver(receiver)
174                 }
175             }
176         }
177         receiverToActions.remove(receiver)
178         logger.logReceiverUnregistered(userId, receiver)
179     }
180 
181     override fun dump(pw: PrintWriter, args: Array<out String>) {
182         pw.indentIfPossible {
183             actionsToActionsReceivers.forEach { (actionFlagsPerm, actionReceiver) ->
184                 println(
185                     "(${actionFlagsPerm.action}: " +
186                         BroadcastDispatcherLogger.flagToString(actionFlagsPerm.flags) +
187                         if (actionFlagsPerm.permission == null) "):"
188                             else ":${actionFlagsPerm.permission}):")
189                 actionReceiver.dump(pw, args)
190             }
191         }
192     }
193 }
194