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.os.Handler 24 import android.os.Looper 25 import android.os.UserHandle 26 import android.util.Log 27 import com.android.systemui.SysuiTestableContext 28 import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger 29 import com.android.systemui.dump.DumpManager 30 import com.android.systemui.settings.UserTracker 31 import java.lang.IllegalStateException 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 66 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 77 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 88 override fun unregisterReceiver(receiver: BroadcastReceiver) { 89 receivers.removeIf { it.receiver == receiver } 90 } 91 92 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 fun sendIntentToMatchingReceiversOnly(context: Context, intent: Intent) { 101 receivers.forEach { 102 if ( 103 it.filter.match( 104 context.contentResolver, 105 intent, 106 /* resolve= */ false, 107 /* logTag= */ "FakeBroadcastDispatcher", 108 ) > 0 109 ) { 110 it.receiver.onReceive(context, intent) 111 } 112 } 113 } 114 115 val numReceiversRegistered: Int 116 get() = receivers.size 117 118 fun cleanUpReceivers(testName: String) { 119 receivers.forEach { 120 val receiver = it.receiver 121 Log.i(testName, "Receiver not unregistered from dispatcher: $receiver") 122 if (shouldFailOnLeakedReceiver) { 123 throw IllegalStateException("Receiver not unregistered from dispatcher: $receiver") 124 } 125 } 126 receivers.clear() 127 } 128 129 private data class InternalReceiver( 130 val receiver: BroadcastReceiver, 131 val filter: IntentFilter, 132 ) 133 } 134