1# Using SystemUI's BroadcastDispatcher
2
3## What is this dispatcher?
4
5This is an internal dispatcher class for global broadcasts that SystemUI components want to receive. The dispatcher consolidates most `BroadcastReceiver` that exist in SystemUI by merging the `IntentFilter` and subscribing a single `BroadcastReceiver` per user with the system.
6
7## Why use the dispatcher?
8
9Having a single `BroadcastReceiver` in SystemUI improves the multi dispatch situation that occurs whenever many classes are filtering for the same intent action. In particular:
10* All supported `BroadcastReceiver` will be aggregated into one single receiver per user.
11* Whenever there is a broadcast, the number of IPC calls from `system_server` into SystemUI will be reduced to one per user (plus one for `USER_ALL`). This is meaninful for actions that are filtered by `BroadcastReceiver` in multiple classes.
12*There could be more than one per user in the case of unsupported filters.*
13* The dispatcher immediately moves out of the main thread upon broadcast, giving back control to `system_server`. This improves the total dispatch time for broadcasts and prevents from timing out.
14* The dispatcher significantly reduces time spent in main thread by handling most operations in a background thread and only using the main thread for subscribing/unsubscribind and dispatching where appropriate.
15
16## Should I use the dispatcher?
17
18The dispatcher supports `BroadcastReceiver` dynamic subscriptions in the following cases:
19
20* The `IntentFilter` contains at least one action.
21* The `IntentFilter` may or may not contain categories.
22* The `IntentFilter` **does not** contain data types, data schemes, data authorities or data paths.
23* The broadcast **is not** gated behind a permission.
24* The broadcast **is not** ordered and doesn't need to set result data.
25
26Additionally, the dispatcher supports the following:
27
28* Subscriptions can be done in any thread.
29* Broadcasts will be dispatched on the main thread (same as `system_server`) by default but a `Handler` can be specified for dispatching
30* A `UserHandle` can be provided to filter the broadcasts by user.
31* Flags (see [`Context#RegisterReceiverFlags`](/core/java/android/content/Context.java)) can be passed for the registration. By default, this will be `Context#RECEIVER_EXPORTED`.
32
33If introducing a new `BroadcastReceiver` (not declared in `AndroidManifest`) that satisfies the constraints above, use the dispatcher to reduce the load on `system_server`.
34
35Additionally, if listening to some broadcast is latency critical (beyond 100ms of latency), consider registering with Context instead.
36
37### A note on sticky broadcasts
38
39Sticky broadcasts are those that have been sent using `Context#sendStickyBroadcast` or `Context#sendStickyBroadcastAsUser`. In general they behave like regular broadcasts, but they are also cached (they may be replaced later) to provide the following two features:
40 * They may be returned by `Context#registerReceiver` if the broadcast is matched by the `IntentFilter`. In case that multiple cached broadcast match the filter, any one of those may be returned.
41 * All cached sticky broadcasts that match the filter will be sent to the just registered `BroadcastReceiver#onReceive`.
42
43Sticky broadcasts are `@Deprecated` since API 24 and the general recommendation is to use regular broadcasts and API that allows to retrieve last known state.
44
45Because of this and in order to provide the necessary optimizations, `BroadcastDispatcher` does not offer support for sticky intents:
46
47* Do not use the dispatcher to obtain the last broadcast (by passing a null `BroadcastReceiver`). `BroadcastDispatcher#registerReceiver` **does not** return the last sticky Intent.
48* Do not expect cached sticky broadcasts to be delivered on registration. This may happen but it's not guaranteed.
49
50## How do I use the dispatcher?
51
52Acquire the dispatcher by using `@Inject` to obtain a `BroadcastDispatcher`. Then, use the following methods in that instance.
53
54### Subscribe
55
56```kotlin
57/**
58 * Register a receiver for broadcast with the dispatcher
59 *
60 * @param receiver A receiver to dispatch the [Intent]
61 * @param filter A filter to determine what broadcasts should be dispatched to this receiver.
62 *               It will only take into account actions and categories for filtering. It must
63 *               have at least one action.
64 * @param executor An executor to dispatch [BroadcastReceiver.onReceive]. Pass null to use an
65 *                 executor in the main thread (default).
66 * @param user A user handle to determine which broadcast should be dispatched to this receiver.
67 *             Pass `null` to use the user of the context (system user in SystemUI).
68 * @param flags Flags to use when registering the receiver. [Context.RECEIVER_EXPORTED] by
69 *              default.
70 * @throws IllegalArgumentException if the filter has other constraints that are not actions or
71 *                                  categories or the filter has no actions.
72 */
73@JvmOverloads
74open fun registerReceiver(
75    receiver: BroadcastReceiver,
76    filter: IntentFilter,
77    executor: Executor? = null,
78    user: UserHandle? = null,
79    @Context.RegisterReceiverFlags flags: Int = Context.RECEIVER_EXPORTED
80)
81```
82
83All subscriptions are done with the same overloaded method. As specified in the doc, in order to pass a `UserHandle` with the default `Executor`, pass `null` for the `Executor`.
84
85In the same way as with `Context`, subscribing the same `BroadcastReceiver` for the same user using different filters will result on two subscriptions, not in replacing the filter.
86
87### Unsubscribe
88
89There are two methods to unsubscribe a given `BroadcastReceiver`. One that will remove it for all users and another where the user can be specified. This allows using separate subscriptions of the same receiver for different users and manipulating them separately.
90
91```kotlin
92/**
93    * Unregister receiver for all users.
94    * <br>
95    * This will remove every registration of [receiver], not those done just with [UserHandle.ALL].
96    *
97    * @param receiver The receiver to unregister. It will be unregistered for all users.
98    */
99fun unregisterReceiver(BroadcastReceiver)
100
101/**
102    * Unregister receiver for a particular user.
103    *
104    * @param receiver The receiver to unregister. It will be unregistered for all users.
105    * @param user The user associated to the registered [receiver]. It can be [UserHandle.ALL].
106    */
107fun unregisterReceiverForUser(BroadcastReceiver, UserHandle)
108```
109
110Unregistering can be done even if the `BroadcastReceiver` has never been registered with `BroadcastDispatcher`. In that case, it is a No-Op.
111
112### A note on goAsync()
113
114If you're processing a broadcast in a background thread, you shouldn't call `goAsync()` and
115`finish()`. The system will keep sysui alive regardless, so it isn't needed.
116