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