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 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 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 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 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