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