xref: /aosp_15_r20/frameworks/base/packages/SystemUI/docs/broadcasts.md (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1*d57664e9SAndroid Build Coastguard Worker# Using SystemUI's BroadcastDispatcher
2*d57664e9SAndroid Build Coastguard Worker
3*d57664e9SAndroid Build Coastguard Worker## What is this dispatcher?
4*d57664e9SAndroid Build Coastguard Worker
5*d57664e9SAndroid Build Coastguard WorkerThis 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*d57664e9SAndroid Build Coastguard Worker
7*d57664e9SAndroid Build Coastguard Worker## Why use the dispatcher?
8*d57664e9SAndroid Build Coastguard Worker
9*d57664e9SAndroid Build Coastguard WorkerHaving 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*d57664e9SAndroid Build Coastguard Worker* All supported `BroadcastReceiver` will be aggregated into one single receiver per user.
11*d57664e9SAndroid Build Coastguard Worker* 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*d57664e9SAndroid Build Coastguard Worker*There could be more than one per user in the case of unsupported filters.*
13*d57664e9SAndroid Build Coastguard Worker* 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*d57664e9SAndroid Build Coastguard Worker* 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*d57664e9SAndroid Build Coastguard Worker
16*d57664e9SAndroid Build Coastguard Worker## Should I use the dispatcher?
17*d57664e9SAndroid Build Coastguard Worker
18*d57664e9SAndroid Build Coastguard WorkerThe dispatcher supports `BroadcastReceiver` dynamic subscriptions in the following cases:
19*d57664e9SAndroid Build Coastguard Worker
20*d57664e9SAndroid Build Coastguard Worker* The `IntentFilter` contains at least one action.
21*d57664e9SAndroid Build Coastguard Worker* The `IntentFilter` may or may not contain categories.
22*d57664e9SAndroid Build Coastguard Worker* The `IntentFilter` **does not** contain data types, data schemes, data authorities or data paths.
23*d57664e9SAndroid Build Coastguard Worker* The broadcast **is not** gated behind a permission.
24*d57664e9SAndroid Build Coastguard Worker* The broadcast **is not** ordered and doesn't need to set result data.
25*d57664e9SAndroid Build Coastguard Worker
26*d57664e9SAndroid Build Coastguard WorkerAdditionally, the dispatcher supports the following:
27*d57664e9SAndroid Build Coastguard Worker
28*d57664e9SAndroid Build Coastguard Worker* Subscriptions can be done in any thread.
29*d57664e9SAndroid Build Coastguard Worker* Broadcasts will be dispatched on the main thread (same as `system_server`) by default but a `Handler` can be specified for dispatching
30*d57664e9SAndroid Build Coastguard Worker* A `UserHandle` can be provided to filter the broadcasts by user.
31*d57664e9SAndroid Build Coastguard Worker* 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*d57664e9SAndroid Build Coastguard Worker
33*d57664e9SAndroid Build Coastguard WorkerIf introducing a new `BroadcastReceiver` (not declared in `AndroidManifest`) that satisfies the constraints above, use the dispatcher to reduce the load on `system_server`.
34*d57664e9SAndroid Build Coastguard Worker
35*d57664e9SAndroid Build Coastguard WorkerAdditionally, if listening to some broadcast is latency critical (beyond 100ms of latency), consider registering with Context instead.
36*d57664e9SAndroid Build Coastguard Worker
37*d57664e9SAndroid Build Coastguard Worker### A note on sticky broadcasts
38*d57664e9SAndroid Build Coastguard Worker
39*d57664e9SAndroid Build Coastguard WorkerSticky 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*d57664e9SAndroid Build Coastguard Worker * 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*d57664e9SAndroid Build Coastguard Worker * All cached sticky broadcasts that match the filter will be sent to the just registered `BroadcastReceiver#onReceive`.
42*d57664e9SAndroid Build Coastguard Worker
43*d57664e9SAndroid Build Coastguard WorkerSticky 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*d57664e9SAndroid Build Coastguard Worker
45*d57664e9SAndroid Build Coastguard WorkerBecause of this and in order to provide the necessary optimizations, `BroadcastDispatcher` does not offer support for sticky intents:
46*d57664e9SAndroid Build Coastguard Worker
47*d57664e9SAndroid Build Coastguard Worker* 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*d57664e9SAndroid Build Coastguard Worker* Do not expect cached sticky broadcasts to be delivered on registration. This may happen but it's not guaranteed.
49*d57664e9SAndroid Build Coastguard Worker
50*d57664e9SAndroid Build Coastguard Worker## How do I use the dispatcher?
51*d57664e9SAndroid Build Coastguard Worker
52*d57664e9SAndroid Build Coastguard WorkerAcquire the dispatcher by using `@Inject` to obtain a `BroadcastDispatcher`. Then, use the following methods in that instance.
53*d57664e9SAndroid Build Coastguard Worker
54*d57664e9SAndroid Build Coastguard Worker### Subscribe
55*d57664e9SAndroid Build Coastguard Worker
56*d57664e9SAndroid Build Coastguard Worker```kotlin
57*d57664e9SAndroid Build Coastguard Worker/**
58*d57664e9SAndroid Build Coastguard Worker * Register a receiver for broadcast with the dispatcher
59*d57664e9SAndroid Build Coastguard Worker *
60*d57664e9SAndroid Build Coastguard Worker * @param receiver A receiver to dispatch the [Intent]
61*d57664e9SAndroid Build Coastguard Worker * @param filter A filter to determine what broadcasts should be dispatched to this receiver.
62*d57664e9SAndroid Build Coastguard Worker *               It will only take into account actions and categories for filtering. It must
63*d57664e9SAndroid Build Coastguard Worker *               have at least one action.
64*d57664e9SAndroid Build Coastguard Worker * @param executor An executor to dispatch [BroadcastReceiver.onReceive]. Pass null to use an
65*d57664e9SAndroid Build Coastguard Worker *                 executor in the main thread (default).
66*d57664e9SAndroid Build Coastguard Worker * @param user A user handle to determine which broadcast should be dispatched to this receiver.
67*d57664e9SAndroid Build Coastguard Worker *             Pass `null` to use the user of the context (system user in SystemUI).
68*d57664e9SAndroid Build Coastguard Worker * @param flags Flags to use when registering the receiver. [Context.RECEIVER_EXPORTED] by
69*d57664e9SAndroid Build Coastguard Worker *              default.
70*d57664e9SAndroid Build Coastguard Worker * @throws IllegalArgumentException if the filter has other constraints that are not actions or
71*d57664e9SAndroid Build Coastguard Worker *                                  categories or the filter has no actions.
72*d57664e9SAndroid Build Coastguard Worker */
73*d57664e9SAndroid Build Coastguard Worker@JvmOverloads
74*d57664e9SAndroid Build Coastguard Workeropen fun registerReceiver(
75*d57664e9SAndroid Build Coastguard Worker    receiver: BroadcastReceiver,
76*d57664e9SAndroid Build Coastguard Worker    filter: IntentFilter,
77*d57664e9SAndroid Build Coastguard Worker    executor: Executor? = null,
78*d57664e9SAndroid Build Coastguard Worker    user: UserHandle? = null,
79*d57664e9SAndroid Build Coastguard Worker    @Context.RegisterReceiverFlags flags: Int = Context.RECEIVER_EXPORTED
80*d57664e9SAndroid Build Coastguard Worker)
81*d57664e9SAndroid Build Coastguard Worker```
82*d57664e9SAndroid Build Coastguard Worker
83*d57664e9SAndroid Build Coastguard WorkerAll 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*d57664e9SAndroid Build Coastguard Worker
85*d57664e9SAndroid Build Coastguard WorkerIn 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*d57664e9SAndroid Build Coastguard Worker
87*d57664e9SAndroid Build Coastguard Worker### Unsubscribe
88*d57664e9SAndroid Build Coastguard Worker
89*d57664e9SAndroid Build Coastguard WorkerThere 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*d57664e9SAndroid Build Coastguard Worker
91*d57664e9SAndroid Build Coastguard Worker```kotlin
92*d57664e9SAndroid Build Coastguard Worker/**
93*d57664e9SAndroid Build Coastguard Worker    * Unregister receiver for all users.
94*d57664e9SAndroid Build Coastguard Worker    * <br>
95*d57664e9SAndroid Build Coastguard Worker    * This will remove every registration of [receiver], not those done just with [UserHandle.ALL].
96*d57664e9SAndroid Build Coastguard Worker    *
97*d57664e9SAndroid Build Coastguard Worker    * @param receiver The receiver to unregister. It will be unregistered for all users.
98*d57664e9SAndroid Build Coastguard Worker    */
99*d57664e9SAndroid Build Coastguard Workerfun unregisterReceiver(BroadcastReceiver)
100*d57664e9SAndroid Build Coastguard Worker
101*d57664e9SAndroid Build Coastguard Worker/**
102*d57664e9SAndroid Build Coastguard Worker    * Unregister receiver for a particular user.
103*d57664e9SAndroid Build Coastguard Worker    *
104*d57664e9SAndroid Build Coastguard Worker    * @param receiver The receiver to unregister. It will be unregistered for all users.
105*d57664e9SAndroid Build Coastguard Worker    * @param user The user associated to the registered [receiver]. It can be [UserHandle.ALL].
106*d57664e9SAndroid Build Coastguard Worker    */
107*d57664e9SAndroid Build Coastguard Workerfun unregisterReceiverForUser(BroadcastReceiver, UserHandle)
108*d57664e9SAndroid Build Coastguard Worker```
109*d57664e9SAndroid Build Coastguard Worker
110*d57664e9SAndroid Build Coastguard WorkerUnregistering can be done even if the `BroadcastReceiver` has never been registered with `BroadcastDispatcher`. In that case, it is a No-Op.
111*d57664e9SAndroid Build Coastguard Worker
112*d57664e9SAndroid Build Coastguard Worker### A note on goAsync()
113*d57664e9SAndroid Build Coastguard Worker
114*d57664e9SAndroid Build Coastguard WorkerIf you're processing a broadcast in a background thread, you shouldn't call `goAsync()` and
115*d57664e9SAndroid Build Coastguard Worker`finish()`. The system will keep sysui alive regardless, so it isn't needed.
116