blob: ddda95ad8fda5957369b8c067699fc9a90126fde [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
30import com.android.systemui.Dumpable
31import java.io.FileDescriptor
32import java.io.PrintWriter
33import java.util.concurrent.atomic.AtomicBoolean
34
35private const val MSG_REGISTER_RECEIVER = 0
36private const val MSG_UNREGISTER_RECEIVER = 1
Fabian Kozynskiff5e91f2019-09-24 15:38:08 -040037private const val TAG = "UserBroadcastDispatcher"
Fabian Kozynski42f86a02019-07-09 12:39:03 -040038private const val DEBUG = false
39
40/**
41 * Broadcast dispatcher for a given user registration [userId].
42 *
43 * Created by [BroadcastDispatcher] as needed by users. The value of [userId] can be
44 * [UserHandle.USER_ALL].
45 */
46class UserBroadcastDispatcher(
47 private val context: Context,
48 private val userId: Int,
49 private val mainHandler: Handler,
50 private val bgLooper: Looper
51) : BroadcastReceiver(), Dumpable {
52
53 private val bgHandler = object : Handler(bgLooper) {
54 override fun handleMessage(msg: Message) {
55 when (msg.what) {
56 MSG_REGISTER_RECEIVER -> handleRegisterReceiver(msg.obj as ReceiverData)
57 MSG_UNREGISTER_RECEIVER -> handleUnregisterReceiver(msg.obj as BroadcastReceiver)
58 else -> Unit
59 }
60 }
61 }
62
63 private val registered = AtomicBoolean(false)
64
65 internal fun isRegistered() = registered.get()
66
67 private val registerReceiver = Runnable {
68 val categories = mutableSetOf<String>()
69 receiverToReceiverData.values.flatten().forEach {
70 it.filter.categoriesIterator()?.asSequence()?.let {
71 categories.addAll(it)
72 }
73 }
74 val intentFilter = IntentFilter().apply {
75 actionsToReceivers.keys.forEach { addAction(it) }
76 categories.forEach { addCategory(it) }
77 }
78
79 if (registered.get()) {
80 context.unregisterReceiver(this)
81 registered.set(false)
82 }
83 // Short interval without receiver, this can be problematic
84 if (intentFilter.countActions() > 0 && !registered.get()) {
85 context.registerReceiverAsUser(
86 this,
87 UserHandle.of(userId),
88 intentFilter,
89 null,
90 bgHandler)
91 registered.set(true)
92 }
93 }
94
95 // Only modify in BG thread
96 private val actionsToReceivers = ArrayMap<String, MutableSet<ReceiverData>>()
97 private val receiverToReceiverData = ArrayMap<BroadcastReceiver, MutableSet<ReceiverData>>()
98
99 override fun onReceive(context: Context, intent: Intent) {
Fabian Kozynskiff5e91f2019-09-24 15:38:08 -0400100 bgHandler.post(HandleBroadcastRunnable(actionsToReceivers, context, intent, pendingResult))
Fabian Kozynski42f86a02019-07-09 12:39:03 -0400101 }
102
103 /**
104 * Register a [ReceiverData] for this user.
105 */
106 fun registerReceiver(receiverData: ReceiverData) {
107 bgHandler.obtainMessage(MSG_REGISTER_RECEIVER, receiverData).sendToTarget()
108 }
109
110 /**
111 * Unregister a given [BroadcastReceiver] for this user.
112 */
113 fun unregisterReceiver(receiver: BroadcastReceiver) {
114 bgHandler.obtainMessage(MSG_UNREGISTER_RECEIVER, receiver).sendToTarget()
115 }
116
117 private fun handleRegisterReceiver(receiverData: ReceiverData) {
118 if (DEBUG) Log.w(TAG, "Register receiver: ${receiverData.receiver}")
119 receiverToReceiverData.getOrPut(receiverData.receiver, { ArraySet() }).add(receiverData)
120 var changed = false
121 // Index the BroadcastReceiver by all its actions, that way it's easier to dispatch given
122 // a received intent.
123 receiverData.filter.actionsIterator().forEach {
124 actionsToReceivers.getOrPut(it) {
125 changed = true
126 ArraySet()
127 }.add(receiverData)
128 }
129 if (changed) {
130 mainHandler.post(registerReceiver)
131 }
132 }
133
134 private fun handleUnregisterReceiver(receiver: BroadcastReceiver) {
135 if (DEBUG) Log.w(TAG, "Unregister receiver: $receiver")
136 val actions = receiverToReceiverData.getOrElse(receiver) { return }
137 .flatMap { it.filter.actionsIterator().asSequence().asIterable() }.toSet()
138 receiverToReceiverData.get(receiver)?.clear()
139 var changed = false
140 actions.forEach { action ->
141 actionsToReceivers.get(action)?.removeIf { it.receiver == receiver }
142 if (actionsToReceivers.get(action)?.isEmpty() ?: false) {
143 changed = true
144 actionsToReceivers.remove(action)
145 }
146 }
147 if (changed) {
148 mainHandler.post(registerReceiver)
149 }
150 }
151
Lucas Dupin64171fe2019-10-30 14:28:29 -0700152 override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
153 pw.println(" Registered=${registered.get()}")
Fabian Kozynski42f86a02019-07-09 12:39:03 -0400154 actionsToReceivers.forEach { (action, list) ->
Lucas Dupin64171fe2019-10-30 14:28:29 -0700155 pw.println(" $action:")
156 list.forEach { pw.println(" ${it.receiver}") }
Fabian Kozynski42f86a02019-07-09 12:39:03 -0400157 }
158 }
159
160 private class HandleBroadcastRunnable(
161 val actionsToReceivers: Map<String, Set<ReceiverData>>,
162 val context: Context,
Fabian Kozynskiff5e91f2019-09-24 15:38:08 -0400163 val intent: Intent,
164 val pendingResult: PendingResult
Fabian Kozynski42f86a02019-07-09 12:39:03 -0400165 ) : Runnable {
166 override fun run() {
167 if (DEBUG) Log.w(TAG, "Dispatching $intent")
168 actionsToReceivers.get(intent.action)
169 ?.filter {
170 it.filter.hasAction(intent.action) &&
171 it.filter.matchCategories(intent.categories) == null }
172 ?.forEach {
173 it.handler.post {
174 if (DEBUG) Log.w(TAG, "Dispatching to ${it.receiver}")
Fabian Kozynskiff5e91f2019-09-24 15:38:08 -0400175 it.receiver.pendingResult = pendingResult
Fabian Kozynski42f86a02019-07-09 12:39:03 -0400176 it.receiver.onReceive(context, intent)
177 }
178 }
179 }
180 }
181}