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.content.BroadcastReceiver
20 import android.content.Context
21 import android.os.Handler
22 import android.os.Looper
23 import android.os.Message
24 import android.os.UserHandle
25 import android.util.ArrayMap
26 import android.util.ArraySet
27 import android.util.Log
28 import androidx.annotation.VisibleForTesting
29 import com.android.internal.util.Preconditions
30 import com.android.systemui.Dumpable
31 import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger
32 import com.android.systemui.util.indentIfPossible
33 import java.io.FileDescriptor
34 import java.io.PrintWriter
35 import java.util.concurrent.Executor
36 import java.util.concurrent.atomic.AtomicInteger
37 
38 private const val MSG_REGISTER_RECEIVER = 0
39 private const val MSG_UNREGISTER_RECEIVER = 1
40 private const val TAG = "UserBroadcastDispatcher"
41 private const val DEBUG = false
42 
43 /**
44  * Broadcast dispatcher for a given user registration [userId].
45  *
46  * Created by [BroadcastDispatcher] as needed by users. The value of [userId] can be
47  * [UserHandle.USER_ALL].
48  */
49 open class UserBroadcastDispatcher(
50     private val context: Context,
51     private val userId: Int,
52     private val bgLooper: Looper,
53     private val bgExecutor: Executor,
54     private val logger: BroadcastDispatcherLogger
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     private val bgHandler = object : Handler(bgLooper) {
65         override fun handleMessage(msg: Message) {
66             when (msg.what) {
67                 MSG_REGISTER_RECEIVER -> handleRegisterReceiver(msg.obj as ReceiverData)
68                 MSG_UNREGISTER_RECEIVER -> handleUnregisterReceiver(msg.obj as BroadcastReceiver)
69                 else -> Unit
70             }
71         }
72     }
73 
74     // Only modify in BG thread
75     @VisibleForTesting
76     internal val actionsToActionsReceivers = ArrayMap<String, ActionReceiver>()
77     private val receiverToActions = ArrayMap<BroadcastReceiver, MutableSet<String>>()
78 
79     @VisibleForTesting
80     internal fun isReceiverReferenceHeld(receiver: BroadcastReceiver): Boolean {
81         return actionsToActionsReceivers.values.any {
82             it.hasReceiver(receiver)
83         } || (receiver in receiverToActions)
84     }
85 
86     /**
87      * Register a [ReceiverData] for this user.
88      */
89     fun registerReceiver(receiverData: ReceiverData) {
90         bgHandler.obtainMessage(MSG_REGISTER_RECEIVER, receiverData).sendToTarget()
91     }
92 
93     /**
94      * Unregister a given [BroadcastReceiver] for this user.
95      */
96     fun unregisterReceiver(receiver: BroadcastReceiver) {
97         bgHandler.obtainMessage(MSG_UNREGISTER_RECEIVER, receiver).sendToTarget()
98     }
99 
100     private fun handleRegisterReceiver(receiverData: ReceiverData) {
101         Preconditions.checkState(bgHandler.looper.isCurrentThread,
102                 "This method should only be called from BG thread")
103         if (DEBUG) Log.w(TAG, "Register receiver: ${receiverData.receiver}")
104         receiverToActions
105                 .getOrPut(receiverData.receiver, { ArraySet() })
106                 .addAll(receiverData.filter.actionsIterator()?.asSequence() ?: emptySequence())
107         receiverData.filter.actionsIterator().forEach {
108             actionsToActionsReceivers
109                     .getOrPut(it, { createActionReceiver(it) })
110                     .addReceiverData(receiverData)
111         }
112         logger.logReceiverRegistered(userId, receiverData.receiver)
113     }
114 
115     @VisibleForTesting
116     internal open fun createActionReceiver(action: String): ActionReceiver {
117         return ActionReceiver(
118                 action,
119                 userId,
120                 {
121                     context.registerReceiverAsUser(this, UserHandle.of(userId), it, null, bgHandler)
122                     logger.logContextReceiverRegistered(userId, it)
123                 },
124                 {
125                     try {
126                         context.unregisterReceiver(this)
127                         logger.logContextReceiverUnregistered(userId, action)
128                     } catch (e: IllegalArgumentException) {
129                         Log.e(TAG, "Trying to unregister unregistered receiver for user $userId, " +
130                                 "action $action",
131                                 IllegalStateException(e))
132                     }
133                 },
134                 bgExecutor,
135                 logger
136         )
137     }
138 
139     private fun handleUnregisterReceiver(receiver: BroadcastReceiver) {
140         Preconditions.checkState(bgHandler.looper.isCurrentThread,
141                 "This method should only be called from BG thread")
142         if (DEBUG) Log.w(TAG, "Unregister receiver: $receiver")
143         receiverToActions.getOrDefault(receiver, mutableSetOf()).forEach {
144             actionsToActionsReceivers.get(it)?.removeReceiver(receiver)
145         }
146         receiverToActions.remove(receiver)
147         logger.logReceiverUnregistered(userId, receiver)
148     }
149 
150     override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
151         pw.indentIfPossible {
152             actionsToActionsReceivers.forEach { (action, actionReceiver) ->
153                 println("$action:")
154                 actionReceiver.dump(fd, pw, args)
155             }
156         }
157     }
158 }
159