1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.statusbar.pipeline.mobile.data.repository.demo
18 
19 import android.telephony.CellSignalStrength
20 import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
21 import android.telephony.TelephonyManager
22 import com.android.systemui.log.table.TableLogBuffer
23 import com.android.systemui.log.table.logDiffsForTable
24 import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
25 import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
26 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType
27 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
28 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
29 import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model.FakeNetworkEventModel
30 import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Companion.COL_CARRIER_ID
31 import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Companion.COL_CARRIER_NETWORK_CHANGE
32 import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Companion.COL_CDMA_LEVEL
33 import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Companion.COL_EMERGENCY
34 import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Companion.COL_IS_GSM
35 import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Companion.COL_IS_IN_SERVICE
36 import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Companion.COL_OPERATOR
37 import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Companion.COL_PRIMARY_LEVEL
38 import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Companion.COL_ROAMING
39 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
40 import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
41 import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.model.FakeWifiEventModel
42 import kotlinx.coroutines.CoroutineScope
43 import kotlinx.coroutines.flow.MutableStateFlow
44 import kotlinx.coroutines.flow.SharingStarted
45 import kotlinx.coroutines.flow.stateIn
46 
47 /**
48  * Demo version of [MobileConnectionRepository]. Note that this class shares all of its flows using
49  * [SharingStarted.WhileSubscribed()] to give the same semantics as using a regular
50  * [MutableStateFlow] while still logging all of the inputs in the same manor as the production
51  * repos.
52  */
53 class DemoMobileConnectionRepository(
54     override val subId: Int,
55     override val tableLogBuffer: TableLogBuffer,
56     val scope: CoroutineScope,
57 ) : MobileConnectionRepository {
58     private val _carrierId = MutableStateFlow(INVALID_SUBSCRIPTION_ID)
59     override val carrierId =
60         _carrierId
61             .logDiffsForTable(
62                 tableLogBuffer,
63                 columnPrefix = "",
64                 columnName = COL_CARRIER_ID,
65                 _carrierId.value,
66             )
67             .stateIn(scope, SharingStarted.WhileSubscribed(), _carrierId.value)
68 
69     private val _isEmergencyOnly = MutableStateFlow(false)
70     override val isEmergencyOnly =
71         _isEmergencyOnly
72             .logDiffsForTable(
73                 tableLogBuffer,
74                 columnPrefix = "",
75                 columnName = COL_EMERGENCY,
76                 _isEmergencyOnly.value
77             )
78             .stateIn(scope, SharingStarted.WhileSubscribed(), _isEmergencyOnly.value)
79 
80     private val _isRoaming = MutableStateFlow(false)
81     override val isRoaming =
82         _isRoaming
83             .logDiffsForTable(
84                 tableLogBuffer,
85                 columnPrefix = "",
86                 columnName = COL_ROAMING,
87                 _isRoaming.value
88             )
89             .stateIn(scope, SharingStarted.WhileSubscribed(), _isRoaming.value)
90 
91     private val _operatorAlphaShort: MutableStateFlow<String?> = MutableStateFlow(null)
92     override val operatorAlphaShort =
93         _operatorAlphaShort
94             .logDiffsForTable(
95                 tableLogBuffer,
96                 columnPrefix = "",
97                 columnName = COL_OPERATOR,
98                 _operatorAlphaShort.value
99             )
100             .stateIn(scope, SharingStarted.WhileSubscribed(), _operatorAlphaShort.value)
101 
102     private val _isInService = MutableStateFlow(false)
103     override val isInService =
104         _isInService
105             .logDiffsForTable(
106                 tableLogBuffer,
107                 columnPrefix = "",
108                 columnName = COL_IS_IN_SERVICE,
109                 _isInService.value
110             )
111             .stateIn(scope, SharingStarted.WhileSubscribed(), _isInService.value)
112 
113     private val _isGsm = MutableStateFlow(false)
114     override val isGsm =
115         _isGsm
116             .logDiffsForTable(
117                 tableLogBuffer,
118                 columnPrefix = "",
119                 columnName = COL_IS_GSM,
120                 _isGsm.value
121             )
122             .stateIn(scope, SharingStarted.WhileSubscribed(), _isGsm.value)
123 
124     private val _cdmaLevel = MutableStateFlow(CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN)
125     override val cdmaLevel =
126         _cdmaLevel
127             .logDiffsForTable(
128                 tableLogBuffer,
129                 columnPrefix = "",
130                 columnName = COL_CDMA_LEVEL,
131                 _cdmaLevel.value
132             )
133             .stateIn(scope, SharingStarted.WhileSubscribed(), _cdmaLevel.value)
134 
135     private val _primaryLevel = MutableStateFlow(CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN)
136     override val primaryLevel =
137         _primaryLevel
138             .logDiffsForTable(
139                 tableLogBuffer,
140                 columnPrefix = "",
141                 columnName = COL_PRIMARY_LEVEL,
142                 _primaryLevel.value
143             )
144             .stateIn(scope, SharingStarted.WhileSubscribed(), _primaryLevel.value)
145 
146     private val _dataConnectionState = MutableStateFlow(DataConnectionState.Disconnected)
147     override val dataConnectionState =
148         _dataConnectionState
149             .logDiffsForTable(tableLogBuffer, columnPrefix = "", _dataConnectionState.value)
150             .stateIn(scope, SharingStarted.WhileSubscribed(), _dataConnectionState.value)
151 
152     private val _dataActivityDirection =
153         MutableStateFlow(
154             DataActivityModel(
155                 hasActivityIn = false,
156                 hasActivityOut = false,
157             )
158         )
159     override val dataActivityDirection =
160         _dataActivityDirection
161             .logDiffsForTable(tableLogBuffer, columnPrefix = "", _dataActivityDirection.value)
162             .stateIn(scope, SharingStarted.WhileSubscribed(), _dataActivityDirection.value)
163 
164     private val _carrierNetworkChangeActive = MutableStateFlow(false)
165     override val carrierNetworkChangeActive =
166         _carrierNetworkChangeActive
167             .logDiffsForTable(
168                 tableLogBuffer,
169                 columnPrefix = "",
170                 columnName = COL_CARRIER_NETWORK_CHANGE,
171                 _carrierNetworkChangeActive.value
172             )
173             .stateIn(scope, SharingStarted.WhileSubscribed(), _carrierNetworkChangeActive.value)
174 
175     private val _resolvedNetworkType: MutableStateFlow<ResolvedNetworkType> =
176         MutableStateFlow(ResolvedNetworkType.UnknownNetworkType)
177     override val resolvedNetworkType =
178         _resolvedNetworkType
179             .logDiffsForTable(tableLogBuffer, columnPrefix = "", _resolvedNetworkType.value)
180             .stateIn(scope, SharingStarted.WhileSubscribed(), _resolvedNetworkType.value)
181 
182     override val numberOfLevels = MutableStateFlow(MobileConnectionRepository.DEFAULT_NUM_LEVELS)
183 
184     override val dataEnabled = MutableStateFlow(true)
185 
186     override val cdmaRoaming = MutableStateFlow(false)
187 
188     override val networkName = MutableStateFlow(NetworkNameModel.IntentDerived(DEMO_CARRIER_NAME))
189 
190     override val carrierName =
191         MutableStateFlow(NetworkNameModel.SubscriptionDerived(DEMO_CARRIER_NAME))
192 
193     override val isAllowedDuringAirplaneMode = MutableStateFlow(false)
194 
195     /**
196      * Process a new demo mobile event. Note that [resolvedNetworkType] must be passed in separately
197      * from the event, due to the requirement to reverse the mobile mappings lookup in the top-level
198      * repository.
199      */
200     fun processDemoMobileEvent(
201         event: FakeNetworkEventModel.Mobile,
202         resolvedNetworkType: ResolvedNetworkType,
203     ) {
204         // This is always true here, because we split out disabled states at the data-source level
205         dataEnabled.value = true
206         networkName.value = NetworkNameModel.IntentDerived(event.name)
207         carrierName.value = NetworkNameModel.SubscriptionDerived("${event.name} ${event.subId}")
208 
209         _carrierId.value = event.carrierId ?: INVALID_SUBSCRIPTION_ID
210 
211         numberOfLevels.value =
212             if (event.inflateStrength) DEFAULT_NUM_LEVELS + 1 else DEFAULT_NUM_LEVELS
213 
214         cdmaRoaming.value = event.roaming
215         _isRoaming.value = event.roaming
216         // TODO(b/261029387): not yet supported
217         _isEmergencyOnly.value = false
218         _operatorAlphaShort.value = event.name
219         _isInService.value = (event.level ?: 0) > 0
220         // TODO(b/261029387): not yet supported
221         _isGsm.value = false
222         _cdmaLevel.value = event.level ?: 0
223         _primaryLevel.value = event.level ?: 0
224         // TODO(b/261029387): not yet supported
225         _dataConnectionState.value = DataConnectionState.Connected
226         _dataActivityDirection.value =
227             (event.activity ?: TelephonyManager.DATA_ACTIVITY_NONE).toMobileDataActivityModel()
228         _carrierNetworkChangeActive.value = event.carrierNetworkChange
229         _resolvedNetworkType.value = resolvedNetworkType
230 
231         isAllowedDuringAirplaneMode.value = false
232     }
233 
234     fun processCarrierMergedEvent(event: FakeWifiEventModel.CarrierMerged) {
235         // This is always true here, because we split out disabled states at the data-source level
236         dataEnabled.value = true
237         networkName.value = NetworkNameModel.IntentDerived(CARRIER_MERGED_NAME)
238         carrierName.value = NetworkNameModel.SubscriptionDerived(CARRIER_MERGED_NAME)
239         // TODO(b/276943904): is carrierId a thing with carrier merged networks?
240         _carrierId.value = INVALID_SUBSCRIPTION_ID
241         numberOfLevels.value = event.numberOfLevels
242         cdmaRoaming.value = false
243         _primaryLevel.value = event.level
244         _cdmaLevel.value = event.level
245         _dataActivityDirection.value = event.activity.toMobileDataActivityModel()
246 
247         // These fields are always the same for carrier-merged networks
248         _resolvedNetworkType.value = ResolvedNetworkType.CarrierMergedNetworkType
249         _dataConnectionState.value = DataConnectionState.Connected
250         _isRoaming.value = false
251         _isEmergencyOnly.value = false
252         _operatorAlphaShort.value = null
253         _isInService.value = true
254         _isGsm.value = false
255         _carrierNetworkChangeActive.value = false
256         isAllowedDuringAirplaneMode.value = true
257     }
258 
259     companion object {
260         private const val DEMO_CARRIER_NAME = "Demo Carrier"
261         private const val CARRIER_MERGED_NAME = "Carrier Merged Network"
262     }
263 }
264