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.BroadcastReceiver.PendingResult 21 import android.content.Context 22 import android.content.Intent 23 import android.content.IntentFilter 24 import android.os.Handler 25 import android.os.Looper 26 import android.os.UserHandle 27 import android.util.Log 28 import com.android.systemui.SysuiTestableContext 29 import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger 30 import com.android.systemui.dump.DumpManager 31 import com.android.systemui.settings.UserTracker 32 import java.util.concurrent.ConcurrentHashMap 33 import java.util.concurrent.Executor 34 35 /** 36 * A fake instance of [BroadcastDispatcher] for tests. 37 * 38 * Important: The *real* broadcast dispatcher will only send intents to receivers if the intent 39 * matches the [IntentFilter] that the [BroadcastReceiver] was registered with. This fake class 40 * exposes [sendIntentToMatchingReceiversOnly] to get the same matching behavior as the real 41 * broadcast dispatcher. 42 */ 43 class FakeBroadcastDispatcher( 44 context: SysuiTestableContext, 45 mainExecutor: Executor, 46 broadcastRunningLooper: Looper, 47 broadcastRunningExecutor: Executor, 48 dumpManager: DumpManager, 49 logger: BroadcastDispatcherLogger, 50 userTracker: UserTracker, 51 private val shouldFailOnLeakedReceiver: Boolean 52 ) : 53 BroadcastDispatcher( 54 context, 55 mainExecutor, 56 broadcastRunningLooper, 57 broadcastRunningExecutor, 58 dumpManager, 59 logger, 60 userTracker, 61 PendingRemovalStore(logger) 62 ) { 63 64 private val receivers: MutableSet<InternalReceiver> = ConcurrentHashMap.newKeySet() 65 registerReceiverWithHandlernull66 override fun registerReceiverWithHandler( 67 receiver: BroadcastReceiver, 68 filter: IntentFilter, 69 handler: Handler, 70 user: UserHandle, 71 @Context.RegisterReceiverFlags flags: Int, 72 permission: String? 73 ) { 74 receivers.add(InternalReceiver(receiver, filter)) 75 } 76 registerReceivernull77 override fun registerReceiver( 78 receiver: BroadcastReceiver, 79 filter: IntentFilter, 80 executor: Executor?, 81 user: UserHandle?, 82 @Context.RegisterReceiverFlags flags: Int, 83 permission: String? 84 ) { 85 receivers.add(InternalReceiver(receiver, filter)) 86 } 87 unregisterReceivernull88 override fun unregisterReceiver(receiver: BroadcastReceiver) { 89 receivers.removeIf { it.receiver == receiver } 90 } 91 unregisterReceiverForUsernull92 override fun unregisterReceiverForUser(receiver: BroadcastReceiver, user: UserHandle) { 93 receivers.removeIf { it.receiver == receiver } 94 } 95 96 /** 97 * Sends the given [intent] to *only* the receivers that were registered with an [IntentFilter] 98 * that matches the intent. 99 * 100 * A non-null [pendingResult] can be used to pass the sending user. 101 */ sendIntentToMatchingReceiversOnlynull102 fun sendIntentToMatchingReceiversOnly( 103 context: Context, 104 intent: Intent, 105 pendingResult: PendingResult? = null 106 ) { 107 receivers.forEach { 108 if ( 109 it.filter.match( 110 context.contentResolver, 111 intent, 112 /* resolve= */ false, 113 /* logTag= */ "FakeBroadcastDispatcher", 114 ) > 0 115 ) { 116 if (pendingResult != null) { 117 it.receiver.pendingResult = pendingResult 118 } 119 it.receiver.onReceive(context, intent) 120 } 121 } 122 } 123 124 val numReceiversRegistered: Int 125 get() = receivers.size 126 cleanUpReceiversnull127 fun cleanUpReceivers(testName: String) { 128 receivers.forEach { 129 val receiver = it.receiver 130 Log.i(testName, "Receiver not unregistered from dispatcher: $receiver") 131 if (shouldFailOnLeakedReceiver) { 132 throw IllegalStateException("Receiver not unregistered from dispatcher: $receiver") 133 } 134 } 135 receivers.clear() 136 } 137 138 private data class InternalReceiver( 139 val receiver: BroadcastReceiver, 140 val filter: IntentFilter, 141 ) 142 143 companion object { fakePendingResultForUsernull144 fun fakePendingResultForUser(userId: Int) = 145 PendingResult( 146 /* resultCode = */ 0, 147 /* resultData = */ "", 148 /* resultExtras = */ null, 149 /* type = */ PendingResult.TYPE_REGISTERED, 150 /* ordered = */ false, 151 /* sticky = */ false, 152 /* token = */ null, 153 userId, 154 /* flags = */ 0, 155 ) 156 } 157 } 158