1 /*
2  * Copyright (C) 2020 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.content.Intent
22 import android.content.IntentFilter
23 import android.util.ArraySet
24 import com.android.systemui.Dumpable
25 import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger
26 import com.android.systemui.util.indentIfPossible
27 import java.io.FileDescriptor
28 import java.io.PrintWriter
29 import java.util.concurrent.Executor
30 import java.util.concurrent.atomic.AtomicInteger
31 
32 /**
33  * Receiver for a given action-userId pair to be used by [UserBroadcastDispatcher].
34  *
35  * Each object of this class will take care of a single Action. It will register if it has at least
36  * one [BroadcastReceiver] added to it, and unregister when none are left.
37  *
38  * It will also re-register if filters with new categories are added. But this should not happen
39  * often.
40  *
41  * This class has no sync controls, so make sure to only make modifications from the background
42  * thread.
43  */
44 class ActionReceiver(
45     private val action: String,
46     private val userId: Int,
47     private val registerAction: BroadcastReceiver.(IntentFilter) -> Unit,
48     private val unregisterAction: BroadcastReceiver.() -> Unit,
49     private val bgExecutor: Executor,
50     private val logger: BroadcastDispatcherLogger
51 ) : BroadcastReceiver(), Dumpable {
52 
53     companion object {
54         val index = AtomicInteger(0)
55     }
56 
57     var registered = false
58         private set
59     private val receiverDatas = ArraySet<ReceiverData>()
60     private val activeCategories = ArraySet<String>()
61 
62     @Throws(IllegalArgumentException::class)
addReceiverDatanull63     fun addReceiverData(receiverData: ReceiverData) {
64         if (!receiverData.filter.hasAction(action)) {
65             throw(IllegalArgumentException("Trying to attach to $action without correct action," +
66                 "receiver: ${receiverData.receiver}"))
67         }
68         val addedCategories = activeCategories
69                 .addAll(receiverData.filter.categoriesIterator()?.asSequence() ?: emptySequence())
70 
71         if (receiverDatas.add(receiverData) && receiverDatas.size == 1) {
72             registerAction(createFilter())
73             registered = true
74         } else if (addedCategories) {
75             unregisterAction()
76             registerAction(createFilter())
77         }
78     }
79 
hasReceivernull80     fun hasReceiver(receiver: BroadcastReceiver): Boolean {
81         return receiverDatas.any { it.receiver == receiver }
82     }
83 
createFilternull84     private fun createFilter(): IntentFilter {
85         val filter = IntentFilter(action)
86         activeCategories.forEach(filter::addCategory)
87         return filter
88     }
89 
removeReceivernull90     fun removeReceiver(receiver: BroadcastReceiver) {
91         if (receiverDatas.removeAll { it.receiver == receiver } &&
92                 receiverDatas.isEmpty() && registered) {
93             unregisterAction()
94             registered = false
95             activeCategories.clear()
96         }
97     }
98 
99     @Throws(IllegalStateException::class)
onReceivenull100     override fun onReceive(context: Context, intent: Intent) {
101         if (intent.action != action) {
102             throw(IllegalStateException("Received intent for ${intent.action} " +
103                 "in receiver for $action}"))
104         }
105         val id = index.getAndIncrement()
106         logger.logBroadcastReceived(id, userId, intent)
107         // Immediately return control to ActivityManager
108         bgExecutor.execute {
109             receiverDatas.forEach {
110                 if (it.filter.matchCategories(intent.categories) == null) {
111                     it.executor.execute {
112                         it.receiver.pendingResult = pendingResult
113                         it.receiver.onReceive(context, intent)
114                         logger.logBroadcastDispatched(id, action, it.receiver)
115                     }
116                 }
117             }
118         }
119     }
120 
dumpnull121     override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
122         pw.indentIfPossible {
123             println("Registered: $registered")
124             println("Receivers:")
125             pw.indentIfPossible {
126                 receiverDatas.forEach {
127                     println(it.receiver)
128                 }
129             }
130             println("Categories: ${activeCategories.joinToString(", ")}")
131         }
132     }
133 }