blob: 24357a740331e354be76f6dbb859fe80e8204f74 [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
Fabian Kozynski5ca7a512019-10-16 19:56:11 +000034import java.util.concurrent.atomic.AtomicInteger
Fabian Kozynski42f86a02019-07-09 12:39:03 -040035
36private const val MSG_REGISTER_RECEIVER = 0
37private const val MSG_UNREGISTER_RECEIVER = 1
Fabian Kozynskiff5e91f2019-09-24 15:38:08 -040038private const val TAG = "UserBroadcastDispatcher"
Fabian Kozynski42f86a02019-07-09 12:39:03 -040039private const val DEBUG = false
40
41/**
42 * Broadcast dispatcher for a given user registration [userId].
43 *
44 * Created by [BroadcastDispatcher] as needed by users. The value of [userId] can be
45 * [UserHandle.USER_ALL].
46 */
47class UserBroadcastDispatcher(
48 private val context: Context,
49 private val userId: Int,
50 private val mainHandler: Handler,
51 private val bgLooper: Looper
52) : BroadcastReceiver(), Dumpable {
53
Fabian Kozynski5ca7a512019-10-16 19:56:11 +000054 companion object {
55 // Used only for debugging. If not debugging, this variable will not be accessed and all
56 // received broadcasts will be tagged with 0. However, as DEBUG is false, nothing will be
57 // logged
58 val index = AtomicInteger(0)
59 }
60
Fabian Kozynski42f86a02019-07-09 12:39:03 -040061 private val bgHandler = object : Handler(bgLooper) {
62 override fun handleMessage(msg: Message) {
63 when (msg.what) {
64 MSG_REGISTER_RECEIVER -> handleRegisterReceiver(msg.obj as ReceiverData)
65 MSG_UNREGISTER_RECEIVER -> handleUnregisterReceiver(msg.obj as BroadcastReceiver)
66 else -> Unit
67 }
68 }
69 }
70
71 private val registered = AtomicBoolean(false)
72
73 internal fun isRegistered() = registered.get()
74
75 private val registerReceiver = Runnable {
76 val categories = mutableSetOf<String>()
77 receiverToReceiverData.values.flatten().forEach {
78 it.filter.categoriesIterator()?.asSequence()?.let {
79 categories.addAll(it)
80 }
81 }
82 val intentFilter = IntentFilter().apply {
83 actionsToReceivers.keys.forEach { addAction(it) }
84 categories.forEach { addCategory(it) }
85 }
86
87 if (registered.get()) {
88 context.unregisterReceiver(this)
89 registered.set(false)
90 }
91 // Short interval without receiver, this can be problematic
92 if (intentFilter.countActions() > 0 && !registered.get()) {
93 context.registerReceiverAsUser(
94 this,
95 UserHandle.of(userId),
96 intentFilter,
97 null,
98 bgHandler)
99 registered.set(true)
100 }
101 }
102
103 // Only modify in BG thread
104 private val actionsToReceivers = ArrayMap<String, MutableSet<ReceiverData>>()
105 private val receiverToReceiverData = ArrayMap<BroadcastReceiver, MutableSet<ReceiverData>>()
106
107 override fun onReceive(context: Context, intent: Intent) {
Fabian Kozynski5ca7a512019-10-16 19:56:11 +0000108 val id = if (DEBUG) index.getAndIncrement() else 0
109 if (DEBUG) Log.w(TAG, "[$id] Received $intent")
110 bgHandler.post(
111 HandleBroadcastRunnable(actionsToReceivers, context, intent, pendingResult, id))
Fabian Kozynski42f86a02019-07-09 12:39:03 -0400112 }
113
114 /**
115 * Register a [ReceiverData] for this user.
116 */
117 fun registerReceiver(receiverData: ReceiverData) {
118 bgHandler.obtainMessage(MSG_REGISTER_RECEIVER, receiverData).sendToTarget()
119 }
120
121 /**
122 * Unregister a given [BroadcastReceiver] for this user.
123 */
124 fun unregisterReceiver(receiver: BroadcastReceiver) {
125 bgHandler.obtainMessage(MSG_UNREGISTER_RECEIVER, receiver).sendToTarget()
126 }
127
128 private fun handleRegisterReceiver(receiverData: ReceiverData) {
129 if (DEBUG) Log.w(TAG, "Register receiver: ${receiverData.receiver}")
130 receiverToReceiverData.getOrPut(receiverData.receiver, { ArraySet() }).add(receiverData)
131 var changed = false
132 // Index the BroadcastReceiver by all its actions, that way it's easier to dispatch given
133 // a received intent.
134 receiverData.filter.actionsIterator().forEach {
135 actionsToReceivers.getOrPut(it) {
136 changed = true
137 ArraySet()
138 }.add(receiverData)
139 }
140 if (changed) {
141 mainHandler.post(registerReceiver)
142 }
143 }
144
145 private fun handleUnregisterReceiver(receiver: BroadcastReceiver) {
146 if (DEBUG) Log.w(TAG, "Unregister receiver: $receiver")
147 val actions = receiverToReceiverData.getOrElse(receiver) { return }
148 .flatMap { it.filter.actionsIterator().asSequence().asIterable() }.toSet()
149 receiverToReceiverData.get(receiver)?.clear()
150 var changed = false
151 actions.forEach { action ->
152 actionsToReceivers.get(action)?.removeIf { it.receiver == receiver }
153 if (actionsToReceivers.get(action)?.isEmpty() ?: false) {
154 changed = true
155 actionsToReceivers.remove(action)
156 }
157 }
158 if (changed) {
159 mainHandler.post(registerReceiver)
160 }
161 }
162
Lucas Dupin64171fe2019-10-30 14:28:29 -0700163 override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
164 pw.println(" Registered=${registered.get()}")
Fabian Kozynski42f86a02019-07-09 12:39:03 -0400165 actionsToReceivers.forEach { (action, list) ->
Lucas Dupin64171fe2019-10-30 14:28:29 -0700166 pw.println(" $action:")
167 list.forEach { pw.println(" ${it.receiver}") }
Fabian Kozynski42f86a02019-07-09 12:39:03 -0400168 }
169 }
170
171 private class HandleBroadcastRunnable(
172 val actionsToReceivers: Map<String, Set<ReceiverData>>,
173 val context: Context,
Fabian Kozynskiff5e91f2019-09-24 15:38:08 -0400174 val intent: Intent,
Fabian Kozynski5ca7a512019-10-16 19:56:11 +0000175 val pendingResult: PendingResult,
176 val index: Int
Fabian Kozynski42f86a02019-07-09 12:39:03 -0400177 ) : Runnable {
178 override fun run() {
Fabian Kozynski5ca7a512019-10-16 19:56:11 +0000179 if (DEBUG) Log.w(TAG, "[$index] Dispatching $intent")
Fabian Kozynski42f86a02019-07-09 12:39:03 -0400180 actionsToReceivers.get(intent.action)
181 ?.filter {
182 it.filter.hasAction(intent.action) &&
183 it.filter.matchCategories(intent.categories) == null }
184 ?.forEach {
185 it.handler.post {
Fabian Kozynski5ca7a512019-10-16 19:56:11 +0000186 if (DEBUG) Log.w(TAG,
187 "[$index] Dispatching ${intent.action} to ${it.receiver}")
Fabian Kozynskiff5e91f2019-09-24 15:38:08 -0400188 it.receiver.pendingResult = pendingResult
Fabian Kozynski42f86a02019-07-09 12:39:03 -0400189 it.receiver.onReceive(context, intent)
190 }
191 }
192 }
193 }
194}