1 /* <lambda>null2 * 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 17 package com.android.systemui.broadcast 18 19 import android.annotation.SuppressLint 20 import android.content.BroadcastReceiver 21 import android.content.Context 22 import android.os.Handler 23 import android.os.Looper 24 import android.os.Trace 25 import android.os.UserHandle 26 import android.util.ArrayMap 27 import android.util.ArraySet 28 import android.util.Log 29 import androidx.annotation.VisibleForTesting 30 import androidx.annotation.WorkerThread 31 import com.android.internal.util.Preconditions 32 import com.android.systemui.Dumpable 33 import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger 34 import com.android.systemui.util.indentIfPossible 35 import java.io.PrintWriter 36 import java.util.concurrent.Executor 37 import java.util.concurrent.atomic.AtomicInteger 38 39 private const val TAG = "UserBroadcastDispatcher" 40 private const val DEBUG = false 41 42 /** 43 * Broadcast dispatcher for a given user registration [userId]. 44 * 45 * Created by [BroadcastDispatcher] as needed by users. The value of [userId] can be 46 * [UserHandle.USER_ALL]. 47 */ 48 open class UserBroadcastDispatcher( 49 private val context: Context, 50 private val userId: Int, 51 private val workerLooper: Looper, 52 private val workerExecutor: Executor, 53 private val logger: BroadcastDispatcherLogger, 54 private val removalPendingStore: PendingRemovalStore 55 ) : Dumpable { 56 57 companion object { 58 // Used only for debugging. If not debugging, this variable will not be accessed and all 59 // received broadcasts will be tagged with 0. However, as DEBUG is false, nothing will be 60 // logged 61 val index = AtomicInteger(0) 62 } 63 64 // Used for key in actionsToActionsReceivers 65 internal data class ReceiverProperties( 66 val action: String, 67 val flags: Int, 68 val permission: String? 69 ) 70 71 private val wrongThreadErrorMsg = "This method should only be called from the worker thread " + 72 "(which is expected to be the BroadcastRunning thread)" 73 private val workerHandler = Handler(workerLooper) 74 75 // Only modify in BroadcastRunning thread 76 @VisibleForTesting 77 internal val actionsToActionsReceivers = ArrayMap<ReceiverProperties, ActionReceiver>() 78 private val receiverToActions = ArrayMap<BroadcastReceiver, MutableSet<String>>() 79 80 @VisibleForTesting 81 internal fun isReceiverReferenceHeld(receiver: BroadcastReceiver): Boolean { 82 return actionsToActionsReceivers.values.any { 83 it.hasReceiver(receiver) 84 } || (receiver in receiverToActions) 85 } 86 87 /** 88 * Register a [ReceiverData] for this user. 89 */ 90 @WorkerThread 91 fun registerReceiver(receiverData: ReceiverData, flags: Int) { 92 handleRegisterReceiver(receiverData, flags) 93 } 94 95 /** 96 * Unregister a given [BroadcastReceiver] for this user. 97 */ 98 @WorkerThread 99 fun unregisterReceiver(receiver: BroadcastReceiver) { 100 handleUnregisterReceiver(receiver) 101 } 102 103 private fun handleRegisterReceiver(receiverData: ReceiverData, flags: Int) { 104 Preconditions.checkState(workerLooper.isCurrentThread, wrongThreadErrorMsg) 105 if (DEBUG) Log.w(TAG, "Register receiver: ${receiverData.receiver}") 106 receiverToActions 107 .getOrPut(receiverData.receiver, { ArraySet() }) 108 .addAll(receiverData.filter.actionsIterator()?.asSequence() ?: emptySequence()) 109 receiverData.filter.actionsIterator().forEach { 110 actionsToActionsReceivers 111 .getOrPut( 112 ReceiverProperties(it, flags, receiverData.permission), 113 { createActionReceiver(it, receiverData.permission, flags) }) 114 .addReceiverData(receiverData) 115 } 116 logger.logReceiverRegistered(userId, receiverData.receiver, flags) 117 } 118 119 @SuppressLint("RegisterReceiverViaContextDetector") 120 @VisibleForTesting 121 internal open fun createActionReceiver( 122 action: String, 123 permission: String?, 124 flags: Int 125 ): ActionReceiver { 126 return ActionReceiver( 127 action, 128 userId, 129 { 130 if (Trace.isEnabled()) { 131 Trace.traceBegin( 132 Trace.TRACE_TAG_APP, "registerReceiver act=$action user=$userId") 133 } 134 context.registerReceiverAsUser( 135 this, 136 UserHandle.of(userId), 137 it, 138 permission, 139 workerHandler, 140 flags 141 ) 142 Trace.endSection() 143 logger.logContextReceiverRegistered(userId, flags, it) 144 }, 145 { 146 try { 147 if (Trace.isEnabled()) { 148 Trace.traceBegin( 149 Trace.TRACE_TAG_APP, 150 "unregisterReceiver act=$action user=$userId") 151 } 152 context.unregisterReceiver(this) 153 Trace.endSection() 154 logger.logContextReceiverUnregistered(userId, action) 155 } catch (e: IllegalArgumentException) { 156 Log.e(TAG, "Trying to unregister unregistered receiver for user $userId, " + 157 "action $action", 158 IllegalStateException(e)) 159 } 160 }, 161 workerExecutor, 162 logger, 163 removalPendingStore::isPendingRemoval 164 ) 165 } 166 167 private fun handleUnregisterReceiver(receiver: BroadcastReceiver) { 168 Preconditions.checkState(workerLooper.isCurrentThread, wrongThreadErrorMsg) 169 if (DEBUG) Log.w(TAG, "Unregister receiver: $receiver") 170 receiverToActions.getOrDefault(receiver, mutableSetOf()).forEach { 171 actionsToActionsReceivers.forEach { (key, value) -> 172 if (key.action == it) { 173 value.removeReceiver(receiver) 174 } 175 } 176 } 177 receiverToActions.remove(receiver) 178 logger.logReceiverUnregistered(userId, receiver) 179 } 180 181 override fun dump(pw: PrintWriter, args: Array<out String>) { 182 pw.indentIfPossible { 183 actionsToActionsReceivers.forEach { (actionFlagsPerm, actionReceiver) -> 184 println( 185 "(${actionFlagsPerm.action}: " + 186 BroadcastDispatcherLogger.flagToString(actionFlagsPerm.flags) + 187 if (actionFlagsPerm.permission == null) "):" 188 else ":${actionFlagsPerm.permission}):") 189 actionReceiver.dump(pw, args) 190 } 191 } 192 } 193 } 194