• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2022 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.prod
18 
19 import android.util.IndentingPrintWriter
20 import androidx.annotation.VisibleForTesting
21 import com.android.systemui.dagger.qualifiers.Background
22 import com.android.systemui.log.table.TableLogBuffer
23 import com.android.systemui.log.table.TableLogBufferFactory
24 import com.android.systemui.log.table.logDiffsForTable
25 import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
26 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
27 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
28 import java.io.PrintWriter
29 import javax.inject.Inject
30 import kotlinx.coroutines.CoroutineScope
31 import kotlinx.coroutines.flow.Flow
32 import kotlinx.coroutines.flow.MutableStateFlow
33 import kotlinx.coroutines.flow.SharingStarted
34 import kotlinx.coroutines.flow.StateFlow
35 import kotlinx.coroutines.flow.flatMapLatest
36 import kotlinx.coroutines.flow.mapLatest
37 import kotlinx.coroutines.flow.stateIn
38 
39 /**
40  * A repository that fully implements a mobile connection.
41  *
42  * This connection could either be a typical mobile connection (see [MobileConnectionRepositoryImpl]
43  * or a carrier merged connection (see [CarrierMergedConnectionRepository]). This repository
44  * switches between the two types of connections based on whether the connection is currently
45  * carrier merged (see [setIsCarrierMerged]).
46  */
47 @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
48 class FullMobileConnectionRepository(
49     override val subId: Int,
50     startingIsCarrierMerged: Boolean,
51     override val tableLogBuffer: TableLogBuffer,
52     subscriptionModel: Flow<SubscriptionModel?>,
53     private val defaultNetworkName: NetworkNameModel,
54     private val networkNameSeparator: String,
55     @Background scope: CoroutineScope,
56     private val mobileRepoFactory: MobileConnectionRepositoryImpl.Factory,
57     private val carrierMergedRepoFactory: CarrierMergedConnectionRepository.Factory,
58 ) : MobileConnectionRepository {
59     /**
60      * Sets whether this connection is a typical mobile connection or a carrier merged connection.
61      */
62     fun setIsCarrierMerged(isCarrierMerged: Boolean) {
63         _isCarrierMerged.value = isCarrierMerged
64     }
65 
66     /**
67      * Returns true if this repo is currently for a carrier merged connection and false otherwise.
68      */
69     @VisibleForTesting fun getIsCarrierMerged() = _isCarrierMerged.value
70 
71     private val _isCarrierMerged = MutableStateFlow(startingIsCarrierMerged)
72     private val isCarrierMerged: StateFlow<Boolean> =
73         _isCarrierMerged
74             .logDiffsForTable(
75                 tableLogBuffer,
76                 columnName = "isCarrierMerged",
77                 initialValue = startingIsCarrierMerged,
78             )
79             .stateIn(scope, SharingStarted.WhileSubscribed(), startingIsCarrierMerged)
80 
81     private val mobileRepo: MobileConnectionRepository by lazy {
82         mobileRepoFactory.build(
83             subId,
84             tableLogBuffer,
85             subscriptionModel,
86             defaultNetworkName,
87             networkNameSeparator,
88         )
89     }
90 
91     private val carrierMergedRepo: MobileConnectionRepository by lazy {
92         carrierMergedRepoFactory.build(subId, tableLogBuffer)
93     }
94 
95     @VisibleForTesting
96     internal val activeRepo: StateFlow<MobileConnectionRepository> = run {
97         val initial =
98             if (startingIsCarrierMerged) {
99                 carrierMergedRepo
100             } else {
101                 mobileRepo
102             }
103 
104         this.isCarrierMerged
105             .mapLatest { isCarrierMerged ->
106                 if (isCarrierMerged) {
107                     carrierMergedRepo
108                 } else {
109                     mobileRepo
110                 }
111             }
112             .stateIn(scope, SharingStarted.WhileSubscribed(), initial)
113     }
114 
115     override val carrierId =
116         activeRepo
117             .flatMapLatest { it.carrierId }
118             .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.carrierId.value)
119 
120     override val cdmaRoaming =
121         activeRepo
122             .flatMapLatest { it.cdmaRoaming }
123             .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.cdmaRoaming.value)
124 
125     override val isEmergencyOnly =
126         activeRepo
127             .flatMapLatest { it.isEmergencyOnly }
128             .logDiffsForTable(
129                 tableLogBuffer,
130                 columnName = COL_EMERGENCY,
131                 initialValue = activeRepo.value.isEmergencyOnly.value,
132             )
133             .stateIn(
134                 scope,
135                 SharingStarted.WhileSubscribed(),
136                 activeRepo.value.isEmergencyOnly.value,
137             )
138 
139     override val isRoaming =
140         activeRepo
141             .flatMapLatest { it.isRoaming }
142             .logDiffsForTable(
143                 tableLogBuffer,
144                 columnName = COL_ROAMING,
145                 initialValue = activeRepo.value.isRoaming.value,
146             )
147             .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.isRoaming.value)
148 
149     override val operatorAlphaShort =
150         activeRepo
151             .flatMapLatest { it.operatorAlphaShort }
152             .logDiffsForTable(
153                 tableLogBuffer,
154                 columnName = COL_OPERATOR,
155                 initialValue = activeRepo.value.operatorAlphaShort.value,
156             )
157             .stateIn(
158                 scope,
159                 SharingStarted.WhileSubscribed(),
160                 activeRepo.value.operatorAlphaShort.value,
161             )
162 
163     override val isInService =
164         activeRepo
165             .flatMapLatest { it.isInService }
166             .logDiffsForTable(
167                 tableLogBuffer,
168                 columnName = COL_IS_IN_SERVICE,
169                 initialValue = activeRepo.value.isInService.value,
170             )
171             .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.isInService.value)
172 
173     override val isNonTerrestrial =
174         activeRepo
175             .flatMapLatest { it.isNonTerrestrial }
176             .logDiffsForTable(
177                 tableLogBuffer,
178                 columnName = COL_IS_NTN,
179                 initialValue = activeRepo.value.isNonTerrestrial.value,
180             )
181             .stateIn(
182                 scope,
183                 SharingStarted.WhileSubscribed(),
184                 activeRepo.value.isNonTerrestrial.value,
185             )
186 
187     override val isGsm =
188         activeRepo
189             .flatMapLatest { it.isGsm }
190             .logDiffsForTable(
191                 tableLogBuffer,
192                 columnName = COL_IS_GSM,
193                 initialValue = activeRepo.value.isGsm.value,
194             )
195             .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.isGsm.value)
196 
197     override val cdmaLevel =
198         activeRepo
199             .flatMapLatest { it.cdmaLevel }
200             .logDiffsForTable(
201                 tableLogBuffer,
202                 columnName = COL_CDMA_LEVEL,
203                 initialValue = activeRepo.value.cdmaLevel.value,
204             )
205             .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.cdmaLevel.value)
206 
207     override val primaryLevel =
208         activeRepo
209             .flatMapLatest { it.primaryLevel }
210             .logDiffsForTable(
211                 tableLogBuffer,
212                 columnName = COL_PRIMARY_LEVEL,
213                 initialValue = activeRepo.value.primaryLevel.value,
214             )
215             .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.primaryLevel.value)
216 
217     override val satelliteLevel: StateFlow<Int> =
218         activeRepo
219             .flatMapLatest { it.satelliteLevel }
220             .logDiffsForTable(
221                 tableLogBuffer,
222                 columnName = COL_SATELLITE_LEVEL,
223                 initialValue = activeRepo.value.satelliteLevel.value,
224             )
225             .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.satelliteLevel.value)
226 
227     override val dataConnectionState =
228         activeRepo
229             .flatMapLatest { it.dataConnectionState }
230             .logDiffsForTable(
231                 tableLogBuffer,
232                 initialValue = activeRepo.value.dataConnectionState.value,
233             )
234             .stateIn(
235                 scope,
236                 SharingStarted.WhileSubscribed(),
237                 activeRepo.value.dataConnectionState.value,
238             )
239 
240     override val dataActivityDirection =
241         activeRepo
242             .flatMapLatest { it.dataActivityDirection }
243             .logDiffsForTable(
244                 tableLogBuffer,
245                 initialValue = activeRepo.value.dataActivityDirection.value,
246             )
247             .stateIn(
248                 scope,
249                 SharingStarted.WhileSubscribed(),
250                 activeRepo.value.dataActivityDirection.value,
251             )
252 
253     override val carrierNetworkChangeActive =
254         activeRepo
255             .flatMapLatest { it.carrierNetworkChangeActive }
256             .logDiffsForTable(
257                 tableLogBuffer,
258                 columnName = COL_CARRIER_NETWORK_CHANGE,
259                 initialValue = activeRepo.value.carrierNetworkChangeActive.value,
260             )
261             .stateIn(
262                 scope,
263                 SharingStarted.WhileSubscribed(),
264                 activeRepo.value.carrierNetworkChangeActive.value,
265             )
266 
267     override val resolvedNetworkType =
268         activeRepo
269             .flatMapLatest { it.resolvedNetworkType }
270             .logDiffsForTable(
271                 tableLogBuffer,
272                 initialValue = activeRepo.value.resolvedNetworkType.value,
273             )
274             .stateIn(
275                 scope,
276                 SharingStarted.WhileSubscribed(),
277                 activeRepo.value.resolvedNetworkType.value,
278             )
279 
280     override val dataEnabled =
281         activeRepo
282             .flatMapLatest { it.dataEnabled }
283             .logDiffsForTable(
284                 tableLogBuffer,
285                 columnName = "dataEnabled",
286                 initialValue = activeRepo.value.dataEnabled.value,
287             )
288             .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.dataEnabled.value)
289 
290     override val inflateSignalStrength =
291         activeRepo
292             .flatMapLatest { it.inflateSignalStrength }
293             .logDiffsForTable(
294                 tableLogBuffer,
295                 columnName = "inflate",
296                 initialValue = activeRepo.value.inflateSignalStrength.value,
297             )
298             .stateIn(
299                 scope,
300                 SharingStarted.WhileSubscribed(),
301                 activeRepo.value.inflateSignalStrength.value,
302             )
303 
304     override val allowNetworkSliceIndicator =
305         activeRepo
306             .flatMapLatest { it.allowNetworkSliceIndicator }
307             .logDiffsForTable(
308                 tableLogBuffer,
309                 columnName = "allowSlice",
310                 initialValue = activeRepo.value.allowNetworkSliceIndicator.value,
311             )
312             .stateIn(
313                 scope,
314                 SharingStarted.WhileSubscribed(),
315                 activeRepo.value.allowNetworkSliceIndicator.value,
316             )
317 
318     override val numberOfLevels =
319         activeRepo
320             .flatMapLatest { it.numberOfLevels }
321             .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.numberOfLevels.value)
322 
323     override val networkName =
324         activeRepo
325             .flatMapLatest { it.networkName }
326             .logDiffsForTable(
327                 tableLogBuffer,
328                 columnPrefix = "intent",
329                 initialValue = activeRepo.value.networkName.value,
330             )
331             .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.networkName.value)
332 
333     override val carrierName =
334         activeRepo
335             .flatMapLatest { it.carrierName }
336             .logDiffsForTable(
337                 tableLogBuffer,
338                 columnPrefix = "sub",
339                 initialValue = activeRepo.value.carrierName.value,
340             )
341             .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.carrierName.value)
342 
343     override val isAllowedDuringAirplaneMode =
344         activeRepo
345             .flatMapLatest { it.isAllowedDuringAirplaneMode }
346             .stateIn(
347                 scope,
348                 SharingStarted.WhileSubscribed(),
349                 activeRepo.value.isAllowedDuringAirplaneMode.value,
350             )
351 
352     override val hasPrioritizedNetworkCapabilities =
353         activeRepo
354             .flatMapLatest { it.hasPrioritizedNetworkCapabilities }
355             .stateIn(
356                 scope,
357                 SharingStarted.WhileSubscribed(),
358                 activeRepo.value.hasPrioritizedNetworkCapabilities.value,
359             )
360 
361     override suspend fun isInEcmMode(): Boolean = activeRepo.value.isInEcmMode()
362 
363     fun dump(pw: PrintWriter) {
364         val ipw = IndentingPrintWriter(pw, "  ")
365 
366         ipw.println("MobileConnectionRepository[$subId]")
367         ipw.increaseIndent()
368 
369         ipw.println("carrierMerged=${_isCarrierMerged.value}")
370 
371         ipw.print("Type (cellular or carrier merged): ")
372         when (activeRepo.value) {
373             is CarrierMergedConnectionRepository -> ipw.println("Carrier merged")
374             is MobileConnectionRepositoryImpl -> ipw.println("Cellular")
375         }
376 
377         ipw.increaseIndent()
378         ipw.println("Provider: ${activeRepo.value}")
379         ipw.decreaseIndent()
380 
381         ipw.decreaseIndent()
382     }
383 
384     class Factory
385     @Inject
386     constructor(
387         @Background private val scope: CoroutineScope,
388         private val logFactory: TableLogBufferFactory,
389         private val mobileRepoFactory: MobileConnectionRepositoryImpl.Factory,
390         private val carrierMergedRepoFactory: CarrierMergedConnectionRepository.Factory,
391     ) {
392         fun build(
393             subId: Int,
394             startingIsCarrierMerged: Boolean,
395             subscriptionModel: Flow<SubscriptionModel?>,
396             defaultNetworkName: NetworkNameModel,
397             networkNameSeparator: String,
398         ): FullMobileConnectionRepository {
399             val mobileLogger =
400                 logFactory.getOrCreate(tableBufferLogName(subId), MOBILE_CONNECTION_BUFFER_SIZE)
401 
402             return FullMobileConnectionRepository(
403                 subId,
404                 startingIsCarrierMerged,
405                 mobileLogger,
406                 subscriptionModel,
407                 defaultNetworkName,
408                 networkNameSeparator,
409                 scope,
410                 mobileRepoFactory,
411                 carrierMergedRepoFactory,
412             )
413         }
414 
415         companion object {
416             /** The buffer size to use for logging. */
417             const val MOBILE_CONNECTION_BUFFER_SIZE = 100
418 
419             /** Returns a log buffer name for a mobile connection with the given [subId]. */
420             fun tableBufferLogName(subId: Int): String = "MobileConnectionLog[$subId]"
421         }
422     }
423 
424     companion object {
425         const val COL_CARRIER_ID = "carrierId"
426         const val COL_CARRIER_NETWORK_CHANGE = "carrierNetworkChangeActive"
427         const val COL_CDMA_LEVEL = "cdmaLevel"
428         const val COL_EMERGENCY = "emergencyOnly"
429         const val COL_IS_NTN = "isNtn"
430         const val COL_IS_GSM = "isGsm"
431         const val COL_IS_IN_SERVICE = "isInService"
432         const val COL_OPERATOR = "operatorName"
433         const val COL_PRIMARY_LEVEL = "primaryLevel"
434         const val COL_SATELLITE_LEVEL = "satelliteLevel"
435         const val COL_ROAMING = "roaming"
436     }
437 }
438