blob: d50666c1b3cf0eec3866b860004600c58b191bc4 [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
31import com.android.internal.util.Preconditions
Fabian Kozynski42f86a02019-07-09 12:39:03 -040032import com.android.systemui.Dumpable
33import java.io.FileDescriptor
34import java.io.PrintWriter
35import java.util.concurrent.atomic.AtomicBoolean
Fabian Kozynski5ca7a512019-10-16 19:56:11 +000036import java.util.concurrent.atomic.AtomicInteger
Fabian Kozynski42f86a02019-07-09 12:39:03 -040037
38private const val MSG_REGISTER_RECEIVER = 0
39private const val MSG_UNREGISTER_RECEIVER = 1
Fabian Kozynskiff5e91f2019-09-24 15:38:08 -040040private const val TAG = "UserBroadcastDispatcher"
Fabian Kozynski42f86a02019-07-09 12:39:03 -040041private 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 */
49class UserBroadcastDispatcher(
50 private val context: Context,
51 private val userId: Int,
52 private val mainHandler: Handler,
53 private val bgLooper: Looper
54) : BroadcastReceiver(), Dumpable {
55
Fabian Kozynski5ca7a512019-10-16 19:56:11 +000056 companion object {
57 // Used only for debugging. If not debugging, this variable will not be accessed and all
58 // received broadcasts will be tagged with 0. However, as DEBUG is false, nothing will be
59 // logged
60 val index = AtomicInteger(0)
61 }
62
Fabian Kozynski42f86a02019-07-09 12:39:03 -040063 private val bgHandler = object : Handler(bgLooper) {
64 override fun handleMessage(msg: Message) {
65 when (msg.what) {
66 MSG_REGISTER_RECEIVER -> handleRegisterReceiver(msg.obj as ReceiverData)
67 MSG_UNREGISTER_RECEIVER -> handleUnregisterReceiver(msg.obj as BroadcastReceiver)
68 else -> Unit
69 }
70 }
71 }
72
73 private val registered = AtomicBoolean(false)
74
75 internal fun isRegistered() = registered.get()
76
Fabian Kozynski213049e2019-11-06 17:23:17 -050077 // Only modify in BG thread
78 private val actionsToReceivers = ArrayMap<String, MutableSet<ReceiverData>>()
79 private val receiverToReceiverData = ArrayMap<BroadcastReceiver, MutableSet<ReceiverData>>()
80
81 // Only call on BG thread as it reads from the maps
82 private fun createFilter(): IntentFilter {
83 Preconditions.checkState(bgHandler.looper.isCurrentThread,
84 "This method should only be called from BG thread")
Fabian Kozynski42f86a02019-07-09 12:39:03 -040085 val categories = mutableSetOf<String>()
86 receiverToReceiverData.values.flatten().forEach {
87 it.filter.categoriesIterator()?.asSequence()?.let {
88 categories.addAll(it)
89 }
90 }
91 val intentFilter = IntentFilter().apply {
Fabian Kozynski213049e2019-11-06 17:23:17 -050092 // The keys of the arrayMap are of type String! so null check is needed
93 actionsToReceivers.keys.forEach { if (it != null) addAction(it) else Unit }
Fabian Kozynski42f86a02019-07-09 12:39:03 -040094 categories.forEach { addCategory(it) }
95 }
Fabian Kozynski213049e2019-11-06 17:23:17 -050096 return intentFilter
Fabian Kozynski42f86a02019-07-09 12:39:03 -040097 }
98
Fabian Kozynski42f86a02019-07-09 12:39:03 -040099 override fun onReceive(context: Context, intent: Intent) {
Fabian Kozynski5ca7a512019-10-16 19:56:11 +0000100 val id = if (DEBUG) index.getAndIncrement() else 0
101 if (DEBUG) Log.w(TAG, "[$id] Received $intent")
102 bgHandler.post(
103 HandleBroadcastRunnable(actionsToReceivers, context, intent, pendingResult, id))
Fabian Kozynski42f86a02019-07-09 12:39:03 -0400104 }
105
106 /**
107 * Register a [ReceiverData] for this user.
108 */
109 fun registerReceiver(receiverData: ReceiverData) {
110 bgHandler.obtainMessage(MSG_REGISTER_RECEIVER, receiverData).sendToTarget()
111 }
112
113 /**
114 * Unregister a given [BroadcastReceiver] for this user.
115 */
116 fun unregisterReceiver(receiver: BroadcastReceiver) {
117 bgHandler.obtainMessage(MSG_UNREGISTER_RECEIVER, receiver).sendToTarget()
118 }
119
120 private fun handleRegisterReceiver(receiverData: ReceiverData) {
Fabian Kozynski213049e2019-11-06 17:23:17 -0500121 Preconditions.checkState(bgHandler.looper.isCurrentThread,
122 "This method should only be called from BG thread")
Fabian Kozynski42f86a02019-07-09 12:39:03 -0400123 if (DEBUG) Log.w(TAG, "Register receiver: ${receiverData.receiver}")
124 receiverToReceiverData.getOrPut(receiverData.receiver, { ArraySet() }).add(receiverData)
125 var changed = false
126 // Index the BroadcastReceiver by all its actions, that way it's easier to dispatch given
127 // a received intent.
128 receiverData.filter.actionsIterator().forEach {
129 actionsToReceivers.getOrPut(it) {
130 changed = true
131 ArraySet()
132 }.add(receiverData)
133 }
134 if (changed) {
Fabian Kozynski213049e2019-11-06 17:23:17 -0500135 createFilterAndRegisterReceiverBG()
Fabian Kozynski42f86a02019-07-09 12:39:03 -0400136 }
137 }
138
139 private fun handleUnregisterReceiver(receiver: BroadcastReceiver) {
Fabian Kozynski213049e2019-11-06 17:23:17 -0500140 Preconditions.checkState(bgHandler.looper.isCurrentThread,
141 "This method should only be called from BG thread")
Fabian Kozynski42f86a02019-07-09 12:39:03 -0400142 if (DEBUG) Log.w(TAG, "Unregister receiver: $receiver")
143 val actions = receiverToReceiverData.getOrElse(receiver) { return }
144 .flatMap { it.filter.actionsIterator().asSequence().asIterable() }.toSet()
145 receiverToReceiverData.get(receiver)?.clear()
146 var changed = false
147 actions.forEach { action ->
148 actionsToReceivers.get(action)?.removeIf { it.receiver == receiver }
149 if (actionsToReceivers.get(action)?.isEmpty() ?: false) {
150 changed = true
151 actionsToReceivers.remove(action)
152 }
153 }
154 if (changed) {
Fabian Kozynski213049e2019-11-06 17:23:17 -0500155 createFilterAndRegisterReceiverBG()
Fabian Kozynski42f86a02019-07-09 12:39:03 -0400156 }
157 }
158
Fabian Kozynski213049e2019-11-06 17:23:17 -0500159 // Only call this from a BG thread
160 private fun createFilterAndRegisterReceiverBG() {
161 val intentFilter = createFilter()
162 mainHandler.post(RegisterReceiverRunnable(intentFilter))
163 }
164
Lucas Dupin64171fe2019-10-30 14:28:29 -0700165 override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
166 pw.println(" Registered=${registered.get()}")
Fabian Kozynski42f86a02019-07-09 12:39:03 -0400167 actionsToReceivers.forEach { (action, list) ->
Lucas Dupin64171fe2019-10-30 14:28:29 -0700168 pw.println(" $action:")
169 list.forEach { pw.println(" ${it.receiver}") }
Fabian Kozynski42f86a02019-07-09 12:39:03 -0400170 }
171 }
172
173 private class HandleBroadcastRunnable(
174 val actionsToReceivers: Map<String, Set<ReceiverData>>,
175 val context: Context,
Fabian Kozynskiff5e91f2019-09-24 15:38:08 -0400176 val intent: Intent,
Fabian Kozynski5ca7a512019-10-16 19:56:11 +0000177 val pendingResult: PendingResult,
178 val index: Int
Fabian Kozynski42f86a02019-07-09 12:39:03 -0400179 ) : Runnable {
180 override fun run() {
Fabian Kozynski5ca7a512019-10-16 19:56:11 +0000181 if (DEBUG) Log.w(TAG, "[$index] Dispatching $intent")
Fabian Kozynski42f86a02019-07-09 12:39:03 -0400182 actionsToReceivers.get(intent.action)
183 ?.filter {
184 it.filter.hasAction(intent.action) &&
185 it.filter.matchCategories(intent.categories) == null }
186 ?.forEach {
187 it.handler.post {
Fabian Kozynski5ca7a512019-10-16 19:56:11 +0000188 if (DEBUG) Log.w(TAG,
189 "[$index] Dispatching ${intent.action} to ${it.receiver}")
Fabian Kozynskiff5e91f2019-09-24 15:38:08 -0400190 it.receiver.pendingResult = pendingResult
Fabian Kozynski42f86a02019-07-09 12:39:03 -0400191 it.receiver.onReceive(context, intent)
192 }
193 }
194 }
195 }
Fabian Kozynski213049e2019-11-06 17:23:17 -0500196
197 private inner class RegisterReceiverRunnable(val intentFilter: IntentFilter) : Runnable {
198
199 /*
200 * Registers and unregisters the BroadcastReceiver
201 *
202 * Must be called from Main Thread
203 */
204 @MainThread
205 override fun run() {
206 if (registered.get()) {
207 context.unregisterReceiver(this@UserBroadcastDispatcher)
208 registered.set(false)
209 }
210 // Short interval without receiver, this can be problematic
211 if (intentFilter.countActions() > 0 && !registered.get()) {
212 context.registerReceiverAsUser(
213 this@UserBroadcastDispatcher,
214 UserHandle.of(userId),
215 intentFilter,
216 null,
217 bgHandler)
218 registered.set(true)
219 }
220 }
221 }
Fabian Kozynski42f86a02019-07-09 12:39:03 -0400222}