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