Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit def01e74 authored by Fabian Kozynski's avatar Fabian Kozynski
Browse files

Add flags to BroadcastDispatcher

Starting soon, one of Context#RECEIVER_NOT_EXPORTED or
Context#RECEIVER_EXPORTED is required when registering broadcasts. This
CL adds an optional argument to BroadcastDispatcher#registerReceiver to
add flags. The default is RECEIVER_EXPORTED, as it's the backwards
compatible behavior.

Note that many actions listened from SystemUI are platform actions and
as such require RECEIVER_EXPORTED.

This CL does not address receivers registered directly with Context.

Test: logs and dumps
Test: atest SystemUITests
Fixes: 198424247
Change-Id: I80d0cee9347c22c4246bd3da8abc9b040d35490a
parent 8bd6b0d1
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ Additionally, the dispatcher supports the following:
* Subscriptions can be done in any thread.
* Broadcasts will be dispatched on the main thread (same as `system_server`) by default but a `Handler` can be specified for dispatching
* A `UserHandle` can be provided to filter the broadcasts by user.
* Flags (see [`Context#RegisterReceiverFlags`](/core/java/android/content/Context.java)) can be passed for the registration. By default, this will be `Context#RECEIVER_EXPORTED`.

If introducing a new `BroadcastReceiver` (not declared in `AndroidManifest`) that satisfies the constraints above, use the dispatcher to reduce the load on `system_server`.

@@ -63,6 +64,8 @@ Acquire the dispatcher by using `@Inject` to obtain a `BroadcastDispatcher`. The
 *                 executor in the main thread (default).
 * @param user A user handle to determine which broadcast should be dispatched to this receiver.
 *             Pass `null` to use the user of the context (system user in SystemUI).
 * @param flags Flags to use when registering the receiver. [Context.RECEIVER_EXPORTED] by
 *              default.             
 * @throws IllegalArgumentException if the filter has other constraints that are not actions or
 *                                  categories or the filter has no actions.
 */
@@ -71,7 +74,8 @@ open fun registerReceiver(
    receiver: BroadcastReceiver,
    filter: IntentFilter,
    executor: Executor? = null,
    user: UserHandle? = null
    user: UserHandle? = null,
    @Context.RegisterReceiverFlags flags: Int = Context.RECEIVER_EXPORTED
)
```

+19 −10
Original line number Diff line number Diff line
@@ -87,8 +87,11 @@ open class BroadcastDispatcher constructor (
     * @param handler A handler to dispatch [BroadcastReceiver.onReceive].
     * @param user A user handle to determine which broadcast should be dispatched to this receiver.
     *             By default, it is the user of the context (system user in SystemUI).
     * @param flags Flags to use when registering the receiver. [Context.RECEIVER_EXPORTED] by
     *              default.
     * @throws IllegalArgumentException if the filter has other constraints that are not actions or
     *                                  categories or the filter has no actions.
     *
     */
    @Deprecated(message = "Replacing Handler for Executor in SystemUI",
            replaceWith = ReplaceWith("registerReceiver(receiver, filter, executor, user)"))
@@ -97,9 +100,10 @@ open class BroadcastDispatcher constructor (
        receiver: BroadcastReceiver,
        filter: IntentFilter,
        handler: Handler,
        user: UserHandle = context.user
        user: UserHandle = context.user,
        @Context.RegisterReceiverFlags flags: Int = Context.RECEIVER_EXPORTED
    ) {
        registerReceiver(receiver, filter, HandlerExecutor(handler), user)
        registerReceiver(receiver, filter, HandlerExecutor(handler), user, flags)
    }

    /**
@@ -113,6 +117,8 @@ open class BroadcastDispatcher constructor (
     *                 executor in the main thread (default).
     * @param user A user handle to determine which broadcast should be dispatched to this receiver.
     *             Pass `null` to use the user of the context (system user in SystemUI).
     * @param flags Flags to use when registering the receiver. [Context.RECEIVER_EXPORTED] by
     *              default.
     * @throws IllegalArgumentException if the filter has other constraints that are not actions or
     *                                  categories or the filter has no actions.
     */
@@ -121,16 +127,18 @@ open class BroadcastDispatcher constructor (
        receiver: BroadcastReceiver,
        filter: IntentFilter,
        executor: Executor? = null,
        user: UserHandle? = null
        user: UserHandle? = null,
        @Context.RegisterReceiverFlags flags: Int = Context.RECEIVER_EXPORTED
    ) {
        checkFilter(filter)
        this.handler
                .obtainMessage(MSG_ADD_RECEIVER, ReceiverData(
        val data = ReceiverData(
                receiver,
                filter,
                executor ?: context.mainExecutor,
                user ?: context.user
                ))
        )
        this.handler
                .obtainMessage(MSG_ADD_RECEIVER, flags, 0, data)
                .sendToTarget()
    }

@@ -188,6 +196,7 @@ open class BroadcastDispatcher constructor (
            when (msg.what) {
                MSG_ADD_RECEIVER -> {
                    val data = msg.obj as ReceiverData
                    val flags = msg.arg1
                    // If the receiver asked to be registered under the current user, we register
                    // under the actual current user.
                    val userId = if (data.user.identifier == UserHandle.USER_CURRENT) {
@@ -201,7 +210,7 @@ open class BroadcastDispatcher constructor (
                    }
                    val uBR = receiversByUser.get(userId, createUBRForUser(userId))
                    receiversByUser.put(userId, uBR)
                    uBR.registerReceiver(data)
                    uBR.registerReceiver(data, flags)
                }

                MSG_REMOVE_RECEIVER -> {
+25 −13
Original line number Diff line number Diff line
@@ -64,7 +64,7 @@ open class UserBroadcastDispatcher(
    private val bgHandler = object : Handler(bgLooper) {
        override fun handleMessage(msg: Message) {
            when (msg.what) {
                MSG_REGISTER_RECEIVER -> handleRegisterReceiver(msg.obj as ReceiverData)
                MSG_REGISTER_RECEIVER -> handleRegisterReceiver(msg.obj as ReceiverData, msg.arg1)
                MSG_UNREGISTER_RECEIVER -> handleUnregisterReceiver(msg.obj as BroadcastReceiver)
                else -> Unit
            }
@@ -73,7 +73,7 @@ open class UserBroadcastDispatcher(

    // Only modify in BG thread
    @VisibleForTesting
    internal val actionsToActionsReceivers = ArrayMap<String, ActionReceiver>()
    internal val actionsToActionsReceivers = ArrayMap<Pair<String, Int>, ActionReceiver>()
    private val receiverToActions = ArrayMap<BroadcastReceiver, MutableSet<String>>()

    @VisibleForTesting
@@ -86,8 +86,8 @@ open class UserBroadcastDispatcher(
    /**
     * Register a [ReceiverData] for this user.
     */
    fun registerReceiver(receiverData: ReceiverData) {
        bgHandler.obtainMessage(MSG_REGISTER_RECEIVER, receiverData).sendToTarget()
    fun registerReceiver(receiverData: ReceiverData, flags: Int) {
        bgHandler.obtainMessage(MSG_REGISTER_RECEIVER, flags, 0, receiverData).sendToTarget()
    }

    /**
@@ -97,7 +97,7 @@ open class UserBroadcastDispatcher(
        bgHandler.obtainMessage(MSG_UNREGISTER_RECEIVER, receiver).sendToTarget()
    }

    private fun handleRegisterReceiver(receiverData: ReceiverData) {
    private fun handleRegisterReceiver(receiverData: ReceiverData, flags: Int) {
        Preconditions.checkState(bgHandler.looper.isCurrentThread,
                "This method should only be called from BG thread")
        if (DEBUG) Log.w(TAG, "Register receiver: ${receiverData.receiver}")
@@ -106,20 +106,27 @@ open class UserBroadcastDispatcher(
                .addAll(receiverData.filter.actionsIterator()?.asSequence() ?: emptySequence())
        receiverData.filter.actionsIterator().forEach {
            actionsToActionsReceivers
                    .getOrPut(it, { createActionReceiver(it) })
                    .getOrPut(it to flags, { createActionReceiver(it, flags) })
                    .addReceiverData(receiverData)
        }
        logger.logReceiverRegistered(userId, receiverData.receiver)
        logger.logReceiverRegistered(userId, receiverData.receiver, flags)
    }

    @VisibleForTesting
    internal open fun createActionReceiver(action: String): ActionReceiver {
    internal open fun createActionReceiver(action: String, flags: Int): ActionReceiver {
        return ActionReceiver(
                action,
                userId,
                {
                    context.registerReceiverAsUser(this, UserHandle.of(userId), it, null, bgHandler)
                    logger.logContextReceiverRegistered(userId, it)
                    context.registerReceiverAsUser(
                            this,
                            UserHandle.of(userId),
                            it,
                            null,
                            bgHandler,
                            flags
                    )
                    logger.logContextReceiverRegistered(userId, flags, it)
                },
                {
                    try {
@@ -141,7 +148,11 @@ open class UserBroadcastDispatcher(
                "This method should only be called from BG thread")
        if (DEBUG) Log.w(TAG, "Unregister receiver: $receiver")
        receiverToActions.getOrDefault(receiver, mutableSetOf()).forEach {
            actionsToActionsReceivers.get(it)?.removeReceiver(receiver)
            actionsToActionsReceivers.forEach { (key, value) ->
                if (key.first == it) {
                    value.removeReceiver(receiver)
                }
            }
        }
        receiverToActions.remove(receiver)
        logger.logReceiverUnregistered(userId, receiver)
@@ -149,8 +160,9 @@ open class UserBroadcastDispatcher(

    override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
        pw.indentIfPossible {
            actionsToActionsReceivers.forEach { (action, actionReceiver) ->
                println("$action:")
            actionsToActionsReceivers.forEach { (actionAndFlags, actionReceiver) ->
                println("(${actionAndFlags.first}: " +
                        "${BroadcastDispatcherLogger.flagToString(actionAndFlags.second)}):")
                actionReceiver.dump(fd, pw, args)
            }
        }
+27 −4
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.systemui.broadcast.logging

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import com.android.systemui.log.LogBuffer
@@ -33,6 +34,25 @@ class BroadcastDispatcherLogger @Inject constructor(
    @BroadcastDispatcherLog private val buffer: LogBuffer
) {

    companion object {
        fun flagToString(@Context.RegisterReceiverFlags flag: Int): String {
            val b = StringBuilder("")
            if (flag and Context.RECEIVER_VISIBLE_TO_INSTANT_APPS != 0) {
                b.append("instant_apps,")
            }
            if (flag and Context.RECEIVER_NOT_EXPORTED != 0) {
                b.append("not_exported,")
            }
            if (flag and Context.RECEIVER_EXPORTED != 0) {
                b.append("exported")
            }
            if (b.isEmpty()) {
                b.append(flag)
            }
            return b.toString()
        }
    }

    fun logBroadcastReceived(broadcastId: Int, user: Int, intent: Intent) {
        val intentString = intent.toString()
        log(INFO, {
@@ -55,13 +75,15 @@ class BroadcastDispatcherLogger @Inject constructor(
        })
    }

    fun logReceiverRegistered(user: Int, receiver: BroadcastReceiver) {
    fun logReceiverRegistered(user: Int, receiver: BroadcastReceiver, flags: Int) {
        val receiverString = receiver.toString()
        val flagsString = flagToString(flags)
        log(INFO, {
            int1 = user
            str1 = receiverString
            str2 = flagsString
        }, {
            "Receiver $str1 registered for user $int1"
            "Receiver $str1 ($str2) registered for user $int1"
        })
    }

@@ -75,7 +97,7 @@ class BroadcastDispatcherLogger @Inject constructor(
        })
    }

    fun logContextReceiverRegistered(user: Int, filter: IntentFilter) {
    fun logContextReceiverRegistered(user: Int, flags: Int, filter: IntentFilter) {
        val actions = filter.actionsIterator().asSequence()
                .joinToString(separator = ",", prefix = "Actions(", postfix = ")")
        val categories = if (filter.countCategories() != 0) {
@@ -91,9 +113,10 @@ class BroadcastDispatcherLogger @Inject constructor(
            } else {
                actions
            }
            str2 = flagToString(flags)
        }, {
            """
                Receiver registered with Context for user $int1.
                Receiver registered with Context for user $int1. Flags=$str2
                $str1
            """.trimIndent()
        })
+59 −5
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger
import com.android.systemui.dump.DumpManager
import com.android.systemui.settings.UserTracker
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
import junit.framework.Assert.assertSame
import org.junit.Before
@@ -54,6 +55,7 @@ class BroadcastDispatcherTest : SysuiTestCase() {
    companion object {
        val user0 = UserHandle.of(0)
        val user1 = UserHandle.of(1)
        const val DEFAULT_FLAG = Context.RECEIVER_EXPORTED

        fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
        const val TEST_ACTION = "TEST_ACTION"
@@ -96,6 +98,7 @@ class BroadcastDispatcherTest : SysuiTestCase() {
        MockitoAnnotations.initMocks(this)
        testableLooper = TestableLooper.get(this)
        executor = FakeExecutor(FakeSystemClock())
        `when`(mockContext.mainExecutor).thenReturn(executor)

        broadcastDispatcher = TestBroadcastDispatcher(
                mockContext,
@@ -121,12 +124,12 @@ class BroadcastDispatcherTest : SysuiTestCase() {

        testableLooper.processAllMessages()

        verify(mockUBRUser0).registerReceiver(capture(argumentCaptor))
        verify(mockUBRUser0).registerReceiver(capture(argumentCaptor), eq(DEFAULT_FLAG))

        assertSame(broadcastReceiver, argumentCaptor.value.receiver)
        assertSame(intentFilter, argumentCaptor.value.filter)

        verify(mockUBRUser1).registerReceiver(capture(argumentCaptor))
        verify(mockUBRUser1).registerReceiver(capture(argumentCaptor), eq(DEFAULT_FLAG))
        assertSame(broadcastReceiverOther, argumentCaptor.value.receiver)
        assertSame(intentFilterOther, argumentCaptor.value.filter)
    }
@@ -139,16 +142,66 @@ class BroadcastDispatcherTest : SysuiTestCase() {

        testableLooper.processAllMessages()

        verify(mockUBRUser0).registerReceiver(capture(argumentCaptor))
        verify(mockUBRUser0).registerReceiver(capture(argumentCaptor), eq(DEFAULT_FLAG))

        assertSame(broadcastReceiver, argumentCaptor.value.receiver)
        assertSame(intentFilter, argumentCaptor.value.filter)

        verify(mockUBRUser1).registerReceiver(capture(argumentCaptor))
        verify(mockUBRUser1).registerReceiver(capture(argumentCaptor), eq(DEFAULT_FLAG))
        assertSame(broadcastReceiverOther, argumentCaptor.value.receiver)
        assertSame(intentFilterOther, argumentCaptor.value.filter)
    }

    @Test
    fun testAddReceiverDefaultFlag_handler() {
        broadcastDispatcher.registerReceiverWithHandler(
                broadcastReceiver, intentFilter, mockHandler)
        testableLooper.processAllMessages()

        verify(mockUBRUser0).registerReceiver(capture(argumentCaptor), eq(DEFAULT_FLAG))

        assertSame(broadcastReceiver, argumentCaptor.value.receiver)
        assertSame(intentFilter, argumentCaptor.value.filter)
    }

    @Test
    fun testAddReceiverCorrectFlag_handler() {
        val flag = 3

        broadcastDispatcher.registerReceiverWithHandler(
                broadcastReceiver, intentFilter, mockHandler, flags = flag)
        testableLooper.processAllMessages()

        verify(mockUBRUser0).registerReceiver(capture(argumentCaptor), eq(flag))

        assertSame(broadcastReceiver, argumentCaptor.value.receiver)
        assertSame(intentFilter, argumentCaptor.value.filter)
    }

    @Test
    fun testAddReceiverDefaultFlag_executor() {
        broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter)
        testableLooper.processAllMessages()

        verify(mockUBRUser0).registerReceiver(capture(argumentCaptor), eq(DEFAULT_FLAG))

        assertSame(broadcastReceiver, argumentCaptor.value.receiver)
        assertSame(intentFilter, argumentCaptor.value.filter)
    }

    @Test
    fun testAddReceiverCorrectFlag_executor() {
        val flag = 3

        broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter, flags = flag)
        testableLooper.processAllMessages()

        verify(mockUBRUser0).registerReceiver(capture(argumentCaptor), eq(flag))

        assertSame(broadcastReceiver, argumentCaptor.value.receiver)
        assertSame(intentFilter, argumentCaptor.value.filter)
    }

    @Test
    fun testRemovingReceiversRemovesFromAllUBR() {
        broadcastDispatcher.registerReceiverWithHandler(broadcastReceiver, intentFilter,
@@ -188,7 +241,8 @@ class BroadcastDispatcherTest : SysuiTestCase() {

        testableLooper.processAllMessages()

        verify(mockUBRUser1).registerReceiver(capture(argumentCaptor))
        verify(mockUBRUser1).registerReceiver(
                capture(argumentCaptor), eq(Context.RECEIVER_EXPORTED))
        assertSame(broadcastReceiver, argumentCaptor.value.receiver)
    }

Loading