xref: /aosp_15_r20/frameworks/base/packages/SystemUI/docs/status-bar-data-pipeline.md (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1*d57664e9SAndroid Build Coastguard Worker# Status Bar Data Pipeline
2*d57664e9SAndroid Build Coastguard Worker
3*d57664e9SAndroid Build Coastguard Worker## Background
4*d57664e9SAndroid Build Coastguard Worker
5*d57664e9SAndroid Build Coastguard WorkerThe status bar is the UI shown at the top of the user's screen that gives them
6*d57664e9SAndroid Build Coastguard Workerinformation about the time, notifications, and system status like mobile
7*d57664e9SAndroid Build Coastguard Workerconectivity and battery level. This document is about the implementation of the
8*d57664e9SAndroid Build Coastguard Workerwifi and mobile system icons on the right side:
9*d57664e9SAndroid Build Coastguard Worker
10*d57664e9SAndroid Build Coastguard Worker![image of status bar](status-bar.png)
11*d57664e9SAndroid Build Coastguard Worker
12*d57664e9SAndroid Build Coastguard WorkerIn Android U, the data pipeline that determines what mobile and wifi icons to
13*d57664e9SAndroid Build Coastguard Workershow in the status bar has been re-written with a new architecture. This format
14*d57664e9SAndroid Build Coastguard Workergenerally follows Android best practices to
15*d57664e9SAndroid Build Coastguard Worker[app architecture](https://developer.android.com/topic/architecture#recommended-app-arch).
16*d57664e9SAndroid Build Coastguard WorkerThis document serves as a guide for the new architecture, and as a guide for how
17*d57664e9SAndroid Build Coastguard WorkerOEMs can add customizations to the new architecture.
18*d57664e9SAndroid Build Coastguard Worker
19*d57664e9SAndroid Build Coastguard Worker## Architecture
20*d57664e9SAndroid Build Coastguard Worker
21*d57664e9SAndroid Build Coastguard WorkerIn the new architecture, there is a separate pipeline for each type of icon. For
22*d57664e9SAndroid Build Coastguard WorkerAndroid U, **only the wifi icon and mobile icons have been implemented in the
23*d57664e9SAndroid Build Coastguard Workernew architecture**.
24*d57664e9SAndroid Build Coastguard Worker
25*d57664e9SAndroid Build Coastguard WorkerAs shown in the Android best practices guide, each new pipeline has a data
26*d57664e9SAndroid Build Coastguard Workerlayer, a domain layer, and a UI layer:
27*d57664e9SAndroid Build Coastguard Worker
28*d57664e9SAndroid Build Coastguard Worker![diagram of UI, domain, and data layers](modern-architecture.png)
29*d57664e9SAndroid Build Coastguard Worker
30*d57664e9SAndroid Build Coastguard WorkerThe classes in the data layer are `repository` instances. The classes in the
31*d57664e9SAndroid Build Coastguard Workerdomain layer are `interactor` instances. The classes in the UI layer are
32*d57664e9SAndroid Build Coastguard Worker`viewmodel` instances and `viewbinder` instances. In this document, "repository"
33*d57664e9SAndroid Build Coastguard Workerand "data layer" will be used interchangably (and the same goes for the other
34*d57664e9SAndroid Build Coastguard Workerlayers).
35*d57664e9SAndroid Build Coastguard Worker
36*d57664e9SAndroid Build Coastguard WorkerThe wifi logic is in `statusbar/pipeline/wifi` and the mobile logic is in
37*d57664e9SAndroid Build Coastguard Worker`statusbar/pipeline/mobile`.
38*d57664e9SAndroid Build Coastguard Worker
39*d57664e9SAndroid Build Coastguard Worker#### Repository (data layer)
40*d57664e9SAndroid Build Coastguard Worker
41*d57664e9SAndroid Build Coastguard WorkerSystem callbacks, broadcast receivers, configuration values are all defined
42*d57664e9SAndroid Build Coastguard Workerhere, and exposed through the appropriate interface. Where appropriate, we
43*d57664e9SAndroid Build Coastguard Workerdefine `Model` objects at this layer so that clients do not have to rely on
44*d57664e9SAndroid Build Coastguard Workersystem-defined interfaces.
45*d57664e9SAndroid Build Coastguard Worker
46*d57664e9SAndroid Build Coastguard Worker#### Interactor (domain layer)
47*d57664e9SAndroid Build Coastguard Worker
48*d57664e9SAndroid Build Coastguard WorkerHere is where we define the business logic and transform the data layer objects
49*d57664e9SAndroid Build Coastguard Workerinto something consumable by the ViewModel classes. For example,
50*d57664e9SAndroid Build Coastguard Worker`MobileIconsInteractor` defines the CBRS filtering logic by exposing a
51*d57664e9SAndroid Build Coastguard Worker`filteredSubscriptions` list.
52*d57664e9SAndroid Build Coastguard Worker
53*d57664e9SAndroid Build Coastguard Worker#### ViewModel (UI layer)
54*d57664e9SAndroid Build Coastguard Worker
55*d57664e9SAndroid Build Coastguard WorkerView models should define the final piece of business logic mapping to UI logic.
56*d57664e9SAndroid Build Coastguard WorkerFor example, the mobile view model checks the `IconInteractor.isRoaming` flow to
57*d57664e9SAndroid Build Coastguard Workerdecide whether or not to show the roaming indicator.
58*d57664e9SAndroid Build Coastguard Worker
59*d57664e9SAndroid Build Coastguard Worker#### ViewBinder
60*d57664e9SAndroid Build Coastguard Worker
61*d57664e9SAndroid Build Coastguard WorkerThese have already been implemented and configured. ViewBinders replace the old
62*d57664e9SAndroid Build Coastguard Worker`applyMobileState` mechanism that existed in the `IconManager` classes of the
63*d57664e9SAndroid Build Coastguard Workerold pipeline. A view binder associates a ViewModel with a View, and keeps the
64*d57664e9SAndroid Build Coastguard Workerview up-to-date with the most recent information from the model.
65*d57664e9SAndroid Build Coastguard Worker
66*d57664e9SAndroid Build Coastguard WorkerAny new fields added to the ViewModel classes need to be equivalently bound to
67*d57664e9SAndroid Build Coastguard Workerthe view here.
68*d57664e9SAndroid Build Coastguard Worker
69*d57664e9SAndroid Build Coastguard Worker### Putting it all together
70*d57664e9SAndroid Build Coastguard Worker
71*d57664e9SAndroid Build Coastguard WorkerPutting that altogether, we have this overall architecture diagram for the
72*d57664e9SAndroid Build Coastguard Workericons:
73*d57664e9SAndroid Build Coastguard Worker
74*d57664e9SAndroid Build Coastguard Worker![diagram of wifi and mobile pipelines](status-bar-pipeline.png)
75*d57664e9SAndroid Build Coastguard Worker
76*d57664e9SAndroid Build Coastguard Worker### Mobile icons architecture
77*d57664e9SAndroid Build Coastguard Worker
78*d57664e9SAndroid Build Coastguard WorkerBecause there can be multiple mobile connections at the same time, the mobile
79*d57664e9SAndroid Build Coastguard Workerpipeline is split up hierarchically. At each level (data, domain, and UI), there
80*d57664e9SAndroid Build Coastguard Workeris a singleton parent class that manages information relevant to **all** mobile
81*d57664e9SAndroid Build Coastguard Workerconnections, and multiple instances of child classes that manage information for
82*d57664e9SAndroid Build Coastguard Workera **single** mobile connection.
83*d57664e9SAndroid Build Coastguard Worker
84*d57664e9SAndroid Build Coastguard WorkerFor example, `MobileConnectionsRepository` is a singleton at the data layer that
85*d57664e9SAndroid Build Coastguard Workerstores information relevant to **all** mobile connections, and it also manages a
86*d57664e9SAndroid Build Coastguard Workerlist of child `MobileConnectionRepository` classes. `MobileConnectionRepository`
87*d57664e9SAndroid Build Coastguard Workeris **not** a singleton, and each individual `MobileConnectionRepository`
88*d57664e9SAndroid Build Coastguard Workerinstance fully qualifies the state of a **single** connection. This pattern is
89*d57664e9SAndroid Build Coastguard Workerrepeated at the `Interactor` and `ViewModel` layers for mobile.
90*d57664e9SAndroid Build Coastguard Worker
91*d57664e9SAndroid Build Coastguard Worker![diagram of mobile parent child relationship](status-bar-mobile-pipeline.png)
92*d57664e9SAndroid Build Coastguard Worker
93*d57664e9SAndroid Build Coastguard WorkerNote: Since there is at most one wifi connection, the wifi pipeline is not split
94*d57664e9SAndroid Build Coastguard Workerup in the same way.
95*d57664e9SAndroid Build Coastguard Worker
96*d57664e9SAndroid Build Coastguard Worker## Customizations
97*d57664e9SAndroid Build Coastguard Worker
98*d57664e9SAndroid Build Coastguard WorkerThe new pipeline completely replaces these classes:
99*d57664e9SAndroid Build Coastguard Worker
100*d57664e9SAndroid Build Coastguard Worker*   `WifiStatusTracker`
101*d57664e9SAndroid Build Coastguard Worker*   `MobileStatusTracker`
102*d57664e9SAndroid Build Coastguard Worker*   `NetworkSignalController` and `NetworkSignalControllerImpl`
103*d57664e9SAndroid Build Coastguard Worker*   `MobileSignalController`
104*d57664e9SAndroid Build Coastguard Worker*   `WifiSignalController`
105*d57664e9SAndroid Build Coastguard Worker*   `StatusBarSignalPolicy` (including `SignalIconState`, `MobileIconState`, and
106*d57664e9SAndroid Build Coastguard Worker    `WifiIconState`)
107*d57664e9SAndroid Build Coastguard Worker
108*d57664e9SAndroid Build Coastguard WorkerAny customizations in any of these classes will need to be migrated to the new
109*d57664e9SAndroid Build Coastguard Workerpipeline. As a general rule, any change that would have gone into
110*d57664e9SAndroid Build Coastguard Worker`NetworkControllerImpl` would be done in `MobileConnectionsRepository`, and any
111*d57664e9SAndroid Build Coastguard Workerchange for `MobileSignalController` can be done in `MobileConnectionRepository`
112*d57664e9SAndroid Build Coastguard Worker(see above on the relationship between those repositories).
113*d57664e9SAndroid Build Coastguard Worker
114*d57664e9SAndroid Build Coastguard Worker### Sample customization: New service
115*d57664e9SAndroid Build Coastguard Worker
116*d57664e9SAndroid Build Coastguard WorkerSome customizations require listening to additional services to get additional
117*d57664e9SAndroid Build Coastguard Workerdata. This new architecture makes it easy to add additional services to the
118*d57664e9SAndroid Build Coastguard Workerstatus bar data pipeline to get icon customizations.
119*d57664e9SAndroid Build Coastguard Worker
120*d57664e9SAndroid Build Coastguard WorkerBelow is a general guide to how a new service should be added. However, there
121*d57664e9SAndroid Build Coastguard Workermay be exceptions to this guide for specific use cases.
122*d57664e9SAndroid Build Coastguard Worker
123*d57664e9SAndroid Build Coastguard Worker1.  In the data layer (`repository` classes), add a new `StateFlow` that listens
124*d57664e9SAndroid Build Coastguard Worker    to the service:
125*d57664e9SAndroid Build Coastguard Worker
126*d57664e9SAndroid Build Coastguard Worker    ```kotlin
127*d57664e9SAndroid Build Coastguard Worker    class MobileConnectionsRepositoryImpl {
128*d57664e9SAndroid Build Coastguard Worker      ...
129*d57664e9SAndroid Build Coastguard Worker      val fooVal: StateFlow<Int> =
130*d57664e9SAndroid Build Coastguard Worker        conflatedCallbackFlow {
131*d57664e9SAndroid Build Coastguard Worker          val callback = object : FooServiceCallback(), FooListener {
132*d57664e9SAndroid Build Coastguard Worker            override fun onFooChanged(foo: Int) {
133*d57664e9SAndroid Build Coastguard Worker              trySend(foo)
134*d57664e9SAndroid Build Coastguard Worker            }
135*d57664e9SAndroid Build Coastguard Worker          }
136*d57664e9SAndroid Build Coastguard Worker
137*d57664e9SAndroid Build Coastguard Worker          fooService.registerCallback(callback)
138*d57664e9SAndroid Build Coastguard Worker
139*d57664e9SAndroid Build Coastguard Worker          awaitClose { fooService.unregisterCallback(callback) }
140*d57664e9SAndroid Build Coastguard Worker        }
141*d57664e9SAndroid Build Coastguard Worker          .stateIn(scope, started = SharingStarted.WhileSubscribed(), FOO_DEFAULT_VAL)
142*d57664e9SAndroid Build Coastguard Worker    }
143*d57664e9SAndroid Build Coastguard Worker    ```
144*d57664e9SAndroid Build Coastguard Worker
145*d57664e9SAndroid Build Coastguard Worker1.  In the domain layer (`interactor` classes), either use this new flow to
146*d57664e9SAndroid Build Coastguard Worker    process values, or just expose the flow as-is for the UI layer.
147*d57664e9SAndroid Build Coastguard Worker
148*d57664e9SAndroid Build Coastguard Worker    For example, if `bar` should only be true when `foo` is positive:
149*d57664e9SAndroid Build Coastguard Worker
150*d57664e9SAndroid Build Coastguard Worker    ```kotlin
151*d57664e9SAndroid Build Coastguard Worker    class MobileIconsInteractor {
152*d57664e9SAndroid Build Coastguard Worker      ...
153*d57664e9SAndroid Build Coastguard Worker      val bar: StateFlow<Boolean> =
154*d57664e9SAndroid Build Coastguard Worker        mobileConnectionsRepo
155*d57664e9SAndroid Build Coastguard Worker          .mapLatest { foo -> foo > 0 }
156*d57664e9SAndroid Build Coastguard Worker          .stateIn(scope, SharingStarted.WhileSubscribed(), initialValue = false)
157*d57664e9SAndroid Build Coastguard Worker    }
158*d57664e9SAndroid Build Coastguard Worker    ```
159*d57664e9SAndroid Build Coastguard Worker
160*d57664e9SAndroid Build Coastguard Worker1.  In the UI layer (`viewmodel` classes), update the existing flows to process
161*d57664e9SAndroid Build Coastguard Worker    the new value from the interactor.
162*d57664e9SAndroid Build Coastguard Worker
163*d57664e9SAndroid Build Coastguard Worker    For example, if the icon should be hidden when `bar` is true:
164*d57664e9SAndroid Build Coastguard Worker
165*d57664e9SAndroid Build Coastguard Worker    ```kotlin
166*d57664e9SAndroid Build Coastguard Worker    class MobileIconViewModel {
167*d57664e9SAndroid Build Coastguard Worker      ...
168*d57664e9SAndroid Build Coastguard Worker      iconId: Flow<Int> = combine(
169*d57664e9SAndroid Build Coastguard Worker        iconInteractor.level,
170*d57664e9SAndroid Build Coastguard Worker        iconInteractor.numberOfLevels,
171*d57664e9SAndroid Build Coastguard Worker        iconInteractor.bar,
172*d57664e9SAndroid Build Coastguard Worker    ) { level, numberOfLevels, bar ->
173*d57664e9SAndroid Build Coastguard Worker      if (bar) {
174*d57664e9SAndroid Build Coastguard Worker        null
175*d57664e9SAndroid Build Coastguard Worker      } else {
176*d57664e9SAndroid Build Coastguard Worker        calcIcon(level, numberOfLevels)
177*d57664e9SAndroid Build Coastguard Worker      }
178*d57664e9SAndroid Build Coastguard Worker    }
179*d57664e9SAndroid Build Coastguard Worker    ```
180*d57664e9SAndroid Build Coastguard Worker
181*d57664e9SAndroid Build Coastguard Worker## Demo mode
182*d57664e9SAndroid Build Coastguard Worker
183*d57664e9SAndroid Build Coastguard WorkerSystemUI demo mode is a first-class citizen in the new pipeline. It is
184*d57664e9SAndroid Build Coastguard Workerimplemented as an entirely separate repository,
185*d57664e9SAndroid Build Coastguard Worker`DemoMobileConnectionsRepository`. When the system moves into demo mode, the
186*d57664e9SAndroid Build Coastguard Workerimplementation of the data layer is switched to the demo repository via the
187*d57664e9SAndroid Build Coastguard Worker`MobileRepositorySwitcher` class.
188*d57664e9SAndroid Build Coastguard Worker
189*d57664e9SAndroid Build Coastguard WorkerBecause the demo mode repositories implement the same interfaces as the
190*d57664e9SAndroid Build Coastguard Workerproduction classes, any changes made above will have to be implemented for demo
191*d57664e9SAndroid Build Coastguard Workermode as well.
192*d57664e9SAndroid Build Coastguard Worker
193*d57664e9SAndroid Build Coastguard Worker1.  Following from above, if `fooVal` is added to the
194*d57664e9SAndroid Build Coastguard Worker    `MobileConnectionsRepository` interface:
195*d57664e9SAndroid Build Coastguard Worker
196*d57664e9SAndroid Build Coastguard Worker    ```kotlin
197*d57664e9SAndroid Build Coastguard Worker    class DemoMobileConnectionsRepository {
198*d57664e9SAndroid Build Coastguard Worker      private val _fooVal = MutableStateFlow(FOO_DEFAULT_VALUE)
199*d57664e9SAndroid Build Coastguard Worker      override val fooVal: StateFlow<Int> = _fooVal.asStateFlow()
200*d57664e9SAndroid Build Coastguard Worker
201*d57664e9SAndroid Build Coastguard Worker      // Process the state. **See below on how to add the command to the CLI**
202*d57664e9SAndroid Build Coastguard Worker      fun processEnabledMobileState(state: Mobile) {
203*d57664e9SAndroid Build Coastguard Worker        ...
204*d57664e9SAndroid Build Coastguard Worker        _fooVal.value = state.fooVal
205*d57664e9SAndroid Build Coastguard Worker      }
206*d57664e9SAndroid Build Coastguard Worker    }
207*d57664e9SAndroid Build Coastguard Worker    ```
208*d57664e9SAndroid Build Coastguard Worker
209*d57664e9SAndroid Build Coastguard Worker1.  (Optional) If you want to enable the command line interface for setting and
210*d57664e9SAndroid Build Coastguard Worker    testing this value in demo mode, you can add parsing logic to
211*d57664e9SAndroid Build Coastguard Worker    `DemoModeMobileConnectionDataSource` and `FakeNetworkEventModel`:
212*d57664e9SAndroid Build Coastguard Worker
213*d57664e9SAndroid Build Coastguard Worker    ```kotlin
214*d57664e9SAndroid Build Coastguard Worker    sealed interface FakeNetworkEventModel {
215*d57664e9SAndroid Build Coastguard Worker      data class Mobile(
216*d57664e9SAndroid Build Coastguard Worker      ...
217*d57664e9SAndroid Build Coastguard Worker      // Add new fields here
218*d57664e9SAndroid Build Coastguard Worker      val fooVal: Int?
219*d57664e9SAndroid Build Coastguard Worker      )
220*d57664e9SAndroid Build Coastguard Worker    }
221*d57664e9SAndroid Build Coastguard Worker    ```
222*d57664e9SAndroid Build Coastguard Worker
223*d57664e9SAndroid Build Coastguard Worker    ```kotlin
224*d57664e9SAndroid Build Coastguard Worker    class DemoModeMobileConnectionDataSource {
225*d57664e9SAndroid Build Coastguard Worker      // Currently, the demo commands are implemented as an extension function on Bundle
226*d57664e9SAndroid Build Coastguard Worker      private fun Bundle.activeMobileEvent(): Mobile {
227*d57664e9SAndroid Build Coastguard Worker        ...
228*d57664e9SAndroid Build Coastguard Worker        val fooVal = getString("fooVal")?.toInt()
229*d57664e9SAndroid Build Coastguard Worker        return Mobile(
230*d57664e9SAndroid Build Coastguard Worker          ...
231*d57664e9SAndroid Build Coastguard Worker          fooVal = fooVal,
232*d57664e9SAndroid Build Coastguard Worker        )
233*d57664e9SAndroid Build Coastguard Worker      }
234*d57664e9SAndroid Build Coastguard Worker    }
235*d57664e9SAndroid Build Coastguard Worker    ```
236*d57664e9SAndroid Build Coastguard Worker
237*d57664e9SAndroid Build Coastguard WorkerIf step 2 is implemented, then you will be able to pass demo commands via the
238*d57664e9SAndroid Build Coastguard Workercommand line:
239*d57664e9SAndroid Build Coastguard Worker
240*d57664e9SAndroid Build Coastguard Worker```
241*d57664e9SAndroid Build Coastguard Workeradb shell am broadcast -a com.android.systemui.demo -e command network -e mobile show -e fooVal <test value>
242*d57664e9SAndroid Build Coastguard Worker```
243*d57664e9SAndroid Build Coastguard Worker
244*d57664e9SAndroid Build Coastguard Worker## Migration plan
245*d57664e9SAndroid Build Coastguard Worker
246*d57664e9SAndroid Build Coastguard WorkerFor Android U, the new pipeline will be enabled and default. However, the old
247*d57664e9SAndroid Build Coastguard Workerpipeline code will still be around just in case the new pipeline doesn’t do well
248*d57664e9SAndroid Build Coastguard Workerin the testing phase.
249*d57664e9SAndroid Build Coastguard Worker
250*d57664e9SAndroid Build Coastguard WorkerFor Android V, the old pipeline will be completely removed and the new pipeline
251*d57664e9SAndroid Build Coastguard Workerwill be the one source of truth.
252*d57664e9SAndroid Build Coastguard Worker
253*d57664e9SAndroid Build Coastguard WorkerOur ask for OEMs is to default to using the new pipeline in Android U. If there
254*d57664e9SAndroid Build Coastguard Workerare customizations that seem difficult to migrate over to the new pipeline,
255*d57664e9SAndroid Build Coastguard Workerplease file a bug with us and we’d be more than happy to consult on the best
256*d57664e9SAndroid Build Coastguard Workersolution. The new pipeline was designed with customizability in mind, so our
257*d57664e9SAndroid Build Coastguard Workerhope is that working the new pipeline will be easier and faster.
258*d57664e9SAndroid Build Coastguard Worker
259*d57664e9SAndroid Build Coastguard WorkerNote: The new pipeline currently only supports the wifi and mobile icons. The
260*d57664e9SAndroid Build Coastguard Workerother system status bar icons may be migrated to a similar architecture in the
261*d57664e9SAndroid Build Coastguard Workerfuture.
262