blob: b2942bb14c6b32f566215fd6f81fde478b2505ba [file] [log] [blame]
Fabian Kozynski42f86a02019-07-09 12:39:03 -04001/*
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
17package com.android.systemui.broadcast
18
19import android.content.BroadcastReceiver
20import android.content.Context
21import android.content.Intent
22import android.content.IntentFilter
23import android.os.Handler
24import android.os.Looper
25import android.os.Message
26import android.os.UserHandle
27import android.util.ArrayMap
28import android.util.ArraySet
29import android.util.Log
Fabian Kozynski213049e2019-11-06 17:23:17 -050030import androidx.annotation.MainThread
Fabian Kozynskie68fa402019-11-21 15:47:23 -050031import androidx.annotation.VisibleForTesting
Fabian Kozynski213049e2019-11-06 17:23:17 -050032import com.android.internal.util.Preconditions
Fabian Kozynski42f86a02019-07-09 12:39:03 -040033import com.android.systemui.Dumpable
34import java.io.FileDescriptor
35import java.io.PrintWriter
36import java.util.concurrent.atomic.AtomicBoolean
Fabian Kozynski5ca7a512019-10-16 19:56:11 +000037import java.util.concurrent.atomic.AtomicInteger
Fabian Kozynski42f86a02019-07-09 12:39:03 -040038
39private const val MSG_REGISTER_RECEIVER = 0
40private const val MSG_UNREGISTER_RECEIVER = 1
Fabian Kozynskiff5e91f2019-09-24 15:38:08 -040041private const val TAG = "UserBroadcastDispatcher"
Fabian Kozynski42f86a02019-07-09 12:39:03 -040042private const val DEBUG = false
43
44/**
45 * Broadcast dispatcher for a given user registration [userId].
46 *
47 * Created by [BroadcastDispatcher] as needed by users. The value of [userId] can be
48 * [UserHandle.USER_ALL].
49 */
50class UserBroadcastDispatcher(
51 private val context: Context,
52 private val userId: Int,
53 private val mainHandler: Handler,
54 private val bgLooper: Looper
55) : BroadcastReceiver(), Dumpable {
56
Fabian Kozynski5ca7a512019-10-16 19:56:11 +000057 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
Fabian Kozynski42f86a02019-07-09 12:39:03 -040064 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 private val registered = AtomicBoolean(false)
75
76 internal fun isRegistered() = registered.get()
77
Fabian Kozynski213049e2019-11-06 17:23:17 -050078 // Only modify in BG thread
79 private val actionsToReceivers = ArrayMap<String, MutableSet<ReceiverData>>()
80 private val receiverToReceiverData = ArrayMap<BroadcastReceiver, MutableSet<ReceiverData>>()
81
Fabian Kozynskie68fa402019-11-21 15:47:23 -050082 @VisibleForTesting
83 internal fun isReceiverReferenceHeld(receiver: BroadcastReceiver): Boolean {
84 return receiverToReceiverData.contains(receiver) ||
85 actionsToReceivers.any {
86 it.value.any { it.receiver == receiver }
87 }
88 }
89
Fabian Kozynski213049e2019-11-06 17:23:17 -050090 // Only call on BG thread as it reads from the maps
91 private fun createFilter(): IntentFilter {
92 Preconditions.checkState(bgHandler.looper.isCurrentThread,
93 "This method should only be called from BG thread")
Fabian Kozynski42f86a02019-07-09 12:39:03 -040094 val categories = mutableSetOf<String>()
95 receiverToReceiverData.values.flatten().forEach {
96 it.filter.categoriesIterator()?.asSequence()?.let {
97 categories.addAll(it)
98 }
99 }
100 val intentFilter = IntentFilter().apply {
Fabian Kozynski213049e2019-11-06 17:23:17 -0500101 // The keys of the arrayMap are of type String! so null check is needed
102 actionsToReceivers.keys.forEach { if (it != null) addAction(it) else Unit }
Fabian Kozynski42f86a02019-07-09 12:39:03 -0400103 categories.forEach { addCategory(it) }
104 }
Fabian Kozynski213049e2019-11-06 17:23:17 -0500105 return intentFilter
Fabian Kozynski42f86a02019-07-09 12:39:03 -0400106 }
107
Fabian Kozynski42f86a02019-07-09 12:39:03 -0400108 override fun onReceive(context: Context, intent: Intent) {
Fabian Kozynski5ca7a512019-10-16 19:56:11 +0000109 val id = if (DEBUG) index.getAndIncrement() else 0
110 if (DEBUG) Log.w(TAG, "[$id] Received $intent")
111 bgHandler.post(
112 HandleBroadcastRunnable(actionsToReceivers, context, intent, pendingResult, id))
Fabian Kozynski42f86a02019-07-09 12:39:03 -0400113 }
114
115 /**
116 * Register a [ReceiverData] for this user.
117 */
118 fun registerReceiver(receiverData: ReceiverData) {
119 bgHandler.obtainMessage(MSG_REGISTER_RECEIVER, receiverData).sendToTarget()
120 }
121
122 /**
123 * Unregister a given [BroadcastReceiver] for this user.
124 */
125 fun unregisterReceiver(receiver: BroadcastReceiver) {
126 bgHandler.obtainMessage(MSG_UNREGISTER_RECEIVER, receiver).sendToTarget()
127 }
128
129 private fun handleRegisterReceiver(receiverData: ReceiverData) {
Fabian Kozynski213049e2019-11-06 17:23:17 -0500130 Preconditions.checkState(bgHandler.looper.isCurrentThread,
131 "This method should only be called from BG thread")
Fabian Kozynski42f86a02019-07-09 12:39:03 -0400132 if (DEBUG) Log.w(TAG, "Register receiver: ${receiverData.receiver}")
133 receiverToReceiverData.getOrPut(receiverData.receiver, { ArraySet() }).add(receiverData)
134 var changed = false
135 // Index the BroadcastReceiver by all its actions, that way it's easier to dispatch given
136 // a received intent.
137 receiverData.filter.actionsIterator().forEach {
138 actionsToReceivers.getOrPut(it) {
139 changed = true
140 ArraySet()
141 }.add(receiverData)
142 }
143 if (changed) {
Fabian Kozynski213049e2019-11-06 17:23:17 -0500144 createFilterAndRegisterReceiverBG()
Fabian Kozynski42f86a02019-07-09 12:39:03 -0400145 }
146 }
147
148 private fun handleUnregisterReceiver(receiver: BroadcastReceiver) {
Fabian Kozynski213049e2019-11-06 17:23:17 -0500149 Preconditions.checkState(bgHandler.looper.isCurrentThread,
150 "This method should only be called from BG thread")
Fabian Kozynski42f86a02019-07-09 12:39:03 -0400151 if (DEBUG) Log.w(TAG, "Unregister receiver: $receiver")
152 val actions = receiverToReceiverData.getOrElse(receiver) { return }
153 .flatMap { it.filter.actionsIterator().asSequence().asIterable() }.toSet()
Fabian Kozynskie68fa402019-11-21 15:47:23 -0500154 receiverToReceiverData.remove(receiver)?.clear()
Fabian Kozynski42f86a02019-07-09 12:39:03 -0400155 var changed = false
156 actions.forEach { action ->
157 actionsToReceivers.get(action)?.removeIf { it.receiver == receiver }
158 if (actionsToReceivers.get(action)?.isEmpty() ?: false) {
159 changed = true
160 actionsToReceivers.remove(action)
161 }
162 }
163 if (changed) {
Fabian Kozynski213049e2019-11-06 17:23:17 -0500164 createFilterAndRegisterReceiverBG()
Fabian Kozynski42f86a02019-07-09 12:39:03 -0400165 }
166 }
167
Fabian Kozynski213049e2019-11-06 17:23:17 -0500168 // Only call this from a BG thread
169 private fun createFilterAndRegisterReceiverBG() {
170 val intentFilter = createFilter()
171 mainHandler.post(RegisterReceiverRunnable(intentFilter))
172 }
173
Lucas Dupin64171fe2019-10-30 14:28:29 -0700174 override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
175 pw.println(" Registered=${registered.get()}")
Fabian Kozynski42f86a02019-07-09 12:39:03 -0400176 actionsToReceivers.forEach { (action, list) ->
Lucas Dupin64171fe2019-10-30 14:28:29 -0700177 pw.println(" $action:")
178 list.forEach { pw.println(" ${it.receiver}") }
Fabian Kozynski42f86a02019-07-09 12:39:03 -0400179 }
180 }
181
182 private class HandleBroadcastRunnable(
183 val actionsToReceivers: Map<String, Set<ReceiverData>>,
184 val context: Context,
Fabian Kozynskiff5e91f2019-09-24 15:38:08 -0400185 val intent: Intent,
Fabian Kozynski5ca7a512019-10-16 19:56:11 +0000186 val pendingResult: PendingResult,
187 val index: Int
Fabian Kozynski42f86a02019-07-09 12:39:03 -0400188 ) : Runnable {
189 override fun run() {
Fabian Kozynski5ca7a512019-10-16 19:56:11 +0000190 if (DEBUG) Log.w(TAG, "[$index] Dispatching $intent")
Fabian Kozynski42f86a02019-07-09 12:39:03 -0400191 actionsToReceivers.get(intent.action)
192 ?.filter {
193 it.filter.hasAction(intent.action) &&
194 it.filter.matchCategories(intent.categories) == null }
195 ?.forEach {
196 it.handler.post {
Fabian Kozynski5ca7a512019-10-16 19:56:11 +0000197 if (DEBUG) Log.w(TAG,
198 "[$index] Dispatching ${intent.action} to ${it.receiver}")
Fabian Kozynskiff5e91f2019-09-24 15:38:08 -0400199 it.receiver.pendingResult = pendingResult
Fabian Kozynski42f86a02019-07-09 12:39:03 -0400200 it.receiver.onReceive(context, intent)
201 }
202 }
203 }
204 }
Fabian Kozynski213049e2019-11-06 17:23:17 -0500205
206 private inner class RegisterReceiverRunnable(val intentFilter: IntentFilter) : Runnable {
207
208 /*
209 * Registers and unregisters the BroadcastReceiver
210 *
211 * Must be called from Main Thread
212 */
213 @MainThread
214 override fun run() {
215 if (registered.get()) {
216 context.unregisterReceiver(this@UserBroadcastDispatcher)
217 registered.set(false)
218 }
219 // Short interval without receiver, this can be problematic
220 if (intentFilter.countActions() > 0 && !registered.get()) {
221 context.registerReceiverAsUser(
222 this@UserBroadcastDispatcher,
223 UserHandle.of(userId),
224 intentFilter,
225 null,
226 bgHandler)
227 registered.set(true)
228 }
229 }
230 }
Fabian Kozynski42f86a02019-07-09 12:39:03 -0400231}