Merge "Ensure that maps are only accessed in BG thread"

This commit is contained in:
Fabian Kozynski
2019-11-07 19:28:03 +00:00
committed by Android (Google) Code Review

View File

@@ -27,6 +27,8 @@ import android.os.UserHandle
import android.util.ArrayMap
import android.util.ArraySet
import android.util.Log
import androidx.annotation.MainThread
import com.android.internal.util.Preconditions
import com.android.systemui.Dumpable
import java.io.FileDescriptor
import java.io.PrintWriter
@@ -72,7 +74,14 @@ class UserBroadcastDispatcher(
internal fun isRegistered() = registered.get()
private val registerReceiver = Runnable {
// Only modify in BG thread
private val actionsToReceivers = ArrayMap<String, MutableSet<ReceiverData>>()
private val receiverToReceiverData = ArrayMap<BroadcastReceiver, MutableSet<ReceiverData>>()
// Only call on BG thread as it reads from the maps
private fun createFilter(): IntentFilter {
Preconditions.checkState(bgHandler.looper.isCurrentThread,
"This method should only be called from BG thread")
val categories = mutableSetOf<String>()
receiverToReceiverData.values.flatten().forEach {
it.filter.categoriesIterator()?.asSequence()?.let {
@@ -80,30 +89,13 @@ class UserBroadcastDispatcher(
}
}
val intentFilter = IntentFilter().apply {
actionsToReceivers.keys.forEach { addAction(it) }
// The keys of the arrayMap are of type String! so null check is needed
actionsToReceivers.keys.forEach { if (it != null) addAction(it) else Unit }
categories.forEach { addCategory(it) }
}
if (registered.get()) {
context.unregisterReceiver(this)
registered.set(false)
}
// Short interval without receiver, this can be problematic
if (intentFilter.countActions() > 0 && !registered.get()) {
context.registerReceiverAsUser(
this,
UserHandle.of(userId),
intentFilter,
null,
bgHandler)
registered.set(true)
}
return intentFilter
}
// Only modify in BG thread
private val actionsToReceivers = ArrayMap<String, MutableSet<ReceiverData>>()
private val receiverToReceiverData = ArrayMap<BroadcastReceiver, MutableSet<ReceiverData>>()
override fun onReceive(context: Context, intent: Intent) {
val id = if (DEBUG) index.getAndIncrement() else 0
if (DEBUG) Log.w(TAG, "[$id] Received $intent")
@@ -126,6 +118,8 @@ class UserBroadcastDispatcher(
}
private fun handleRegisterReceiver(receiverData: ReceiverData) {
Preconditions.checkState(bgHandler.looper.isCurrentThread,
"This method should only be called from BG thread")
if (DEBUG) Log.w(TAG, "Register receiver: ${receiverData.receiver}")
receiverToReceiverData.getOrPut(receiverData.receiver, { ArraySet() }).add(receiverData)
var changed = false
@@ -138,11 +132,13 @@ class UserBroadcastDispatcher(
}.add(receiverData)
}
if (changed) {
mainHandler.post(registerReceiver)
createFilterAndRegisterReceiverBG()
}
}
private fun handleUnregisterReceiver(receiver: BroadcastReceiver) {
Preconditions.checkState(bgHandler.looper.isCurrentThread,
"This method should only be called from BG thread")
if (DEBUG) Log.w(TAG, "Unregister receiver: $receiver")
val actions = receiverToReceiverData.getOrElse(receiver) { return }
.flatMap { it.filter.actionsIterator().asSequence().asIterable() }.toSet()
@@ -156,10 +152,16 @@ class UserBroadcastDispatcher(
}
}
if (changed) {
mainHandler.post(registerReceiver)
createFilterAndRegisterReceiverBG()
}
}
// Only call this from a BG thread
private fun createFilterAndRegisterReceiverBG() {
val intentFilter = createFilter()
mainHandler.post(RegisterReceiverRunnable(intentFilter))
}
override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
pw.println(" Registered=${registered.get()}")
actionsToReceivers.forEach { (action, list) ->
@@ -191,4 +193,30 @@ class UserBroadcastDispatcher(
}
}
}
private inner class RegisterReceiverRunnable(val intentFilter: IntentFilter) : Runnable {
/*
* Registers and unregisters the BroadcastReceiver
*
* Must be called from Main Thread
*/
@MainThread
override fun run() {
if (registered.get()) {
context.unregisterReceiver(this@UserBroadcastDispatcher)
registered.set(false)
}
// Short interval without receiver, this can be problematic
if (intentFilter.countActions() > 0 && !registered.get()) {
context.registerReceiverAsUser(
this@UserBroadcastDispatcher,
UserHandle.of(userId),
intentFilter,
null,
bgHandler)
registered.set(true)
}
}
}
}