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