• 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.annotation.SuppressLint
20 import android.content.BroadcastReceiver
21 import android.content.Context
22 import android.content.Intent
23 import android.content.IntentFilter
24 import android.telephony.CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN
25 import android.telephony.CellSignalStrengthCdma
26 import android.telephony.ServiceState
27 import android.telephony.SignalStrength
28 import android.telephony.SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX
29 import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
30 import android.telephony.TelephonyCallback
31 import android.telephony.TelephonyDisplayInfo
32 import android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE
33 import android.telephony.TelephonyManager
34 import android.telephony.TelephonyManager.ERI_FLASH
35 import android.telephony.TelephonyManager.ERI_ON
36 import android.telephony.TelephonyManager.EXTRA_SUBSCRIPTION_ID
37 import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN
38 import android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID
39 import com.android.settingslib.Utils
40 import com.android.systemui.broadcast.BroadcastDispatcher
41 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
42 import com.android.systemui.dagger.qualifiers.Application
43 import com.android.systemui.dagger.qualifiers.Background
44 import com.android.systemui.log.table.TableLogBuffer
45 import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger
46 import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState.Disconnected
47 import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
48 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.DefaultNetworkType
49 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.OverrideNetworkType
50 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.UnknownNetworkType
51 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
52 import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig
53 import com.android.systemui.statusbar.pipeline.mobile.data.model.toDataConnectionType
54 import com.android.systemui.statusbar.pipeline.mobile.data.model.toNetworkNameModel
55 import com.android.systemui.statusbar.pipeline.mobile.data.repository.CarrierConfigRepository
56 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
57 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
58 import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
59 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
60 import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
61 import javax.inject.Inject
62 import kotlinx.coroutines.CoroutineDispatcher
63 import kotlinx.coroutines.CoroutineScope
64 import kotlinx.coroutines.ExperimentalCoroutinesApi
65 import kotlinx.coroutines.asExecutor
66 import kotlinx.coroutines.channels.awaitClose
67 import kotlinx.coroutines.flow.Flow
68 import kotlinx.coroutines.flow.MutableStateFlow
69 import kotlinx.coroutines.flow.SharingStarted
70 import kotlinx.coroutines.flow.StateFlow
71 import kotlinx.coroutines.flow.asStateFlow
72 import kotlinx.coroutines.flow.callbackFlow
73 import kotlinx.coroutines.flow.filter
74 import kotlinx.coroutines.flow.map
75 import kotlinx.coroutines.flow.mapLatest
76 import kotlinx.coroutines.flow.mapNotNull
77 import kotlinx.coroutines.flow.onStart
78 import kotlinx.coroutines.flow.scan
79 import kotlinx.coroutines.flow.stateIn
80 
81 /**
82  * A repository implementation for a typical mobile connection (as opposed to a carrier merged
83  * connection -- see [CarrierMergedConnectionRepository]).
84  */
85 @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
86 @OptIn(ExperimentalCoroutinesApi::class)
87 class MobileConnectionRepositoryImpl(
88     override val subId: Int,
89     private val context: Context,
90     subscriptionModel: StateFlow<SubscriptionModel?>,
91     defaultNetworkName: NetworkNameModel,
92     networkNameSeparator: String,
93     private val telephonyManager: TelephonyManager,
94     systemUiCarrierConfig: SystemUiCarrierConfig,
95     broadcastDispatcher: BroadcastDispatcher,
96     private val mobileMappingsProxy: MobileMappingsProxy,
97     bgDispatcher: CoroutineDispatcher,
98     logger: MobileInputLogger,
99     override val tableLogBuffer: TableLogBuffer,
100     scope: CoroutineScope,
101 ) : MobileConnectionRepository {
102     init {
103         if (telephonyManager.subscriptionId != subId) {
104             throw IllegalStateException(
105                 "MobileRepo: TelephonyManager should be created with subId($subId). " +
106                     "Found ${telephonyManager.subscriptionId} instead."
107             )
108         }
109     }
110 
111     /**
112      * This flow defines the single shared connection to system_server via TelephonyCallback. Any
113      * new callback should be added to this listener and funneled through callbackEvents via a data
114      * class. See [CallbackEvent] for defining new callbacks.
115      *
116      * The reason we need to do this is because TelephonyManager limits the number of registered
117      * listeners per-process, so we don't want to create a new listener for every callback.
118      *
119      * A note on the design for back pressure here: We don't control _which_ telephony callback
120      * comes in first, since we register every relevant bit of information as a batch. E.g., if a
121      * downstream starts collecting on a field which is backed by
122      * [TelephonyCallback.ServiceStateListener], it's not possible for us to guarantee that _that_
123      * callback comes in -- the first callback could very well be
124      * [TelephonyCallback.DataActivityListener], which would promptly be dropped if we didn't keep
125      * it tracked. We use the [scan] operator here to track the most recent callback of _each type_
126      * here. See [TelephonyCallbackState] to see how the callbacks are stored.
127      */
128     private val callbackEvents: StateFlow<TelephonyCallbackState> = run {
129         val initial = TelephonyCallbackState()
130         callbackFlow {
131                 val callback =
132                     object :
133                         TelephonyCallback(),
134                         TelephonyCallback.ServiceStateListener,
135                         TelephonyCallback.SignalStrengthsListener,
136                         TelephonyCallback.DataConnectionStateListener,
137                         TelephonyCallback.DataActivityListener,
138                         TelephonyCallback.CarrierNetworkListener,
139                         TelephonyCallback.DisplayInfoListener,
140                         TelephonyCallback.DataEnabledListener {
141                         override fun onServiceStateChanged(serviceState: ServiceState) {
142                             logger.logOnServiceStateChanged(serviceState, subId)
143                             trySend(CallbackEvent.OnServiceStateChanged(serviceState))
144                         }
145 
146                         override fun onSignalStrengthsChanged(signalStrength: SignalStrength) {
147                             logger.logOnSignalStrengthsChanged(signalStrength, subId)
148                             trySend(CallbackEvent.OnSignalStrengthChanged(signalStrength))
149                         }
150 
151                         override fun onDataConnectionStateChanged(
152                             dataState: Int,
153                             networkType: Int
154                         ) {
155                             logger.logOnDataConnectionStateChanged(dataState, networkType, subId)
156                             trySend(CallbackEvent.OnDataConnectionStateChanged(dataState))
157                         }
158 
159                         override fun onDataActivity(direction: Int) {
160                             logger.logOnDataActivity(direction, subId)
161                             trySend(CallbackEvent.OnDataActivity(direction))
162                         }
163 
164                         override fun onCarrierNetworkChange(active: Boolean) {
165                             logger.logOnCarrierNetworkChange(active, subId)
166                             trySend(CallbackEvent.OnCarrierNetworkChange(active))
167                         }
168 
169                         override fun onDisplayInfoChanged(
170                             telephonyDisplayInfo: TelephonyDisplayInfo
171                         ) {
172                             logger.logOnDisplayInfoChanged(telephonyDisplayInfo, subId)
173                             trySend(CallbackEvent.OnDisplayInfoChanged(telephonyDisplayInfo))
174                         }
175 
176                         override fun onDataEnabledChanged(enabled: Boolean, reason: Int) {
177                             logger.logOnDataEnabledChanged(enabled, subId)
178                             trySend(CallbackEvent.OnDataEnabledChanged(enabled))
179                         }
180                     }
181                 telephonyManager.registerTelephonyCallback(bgDispatcher.asExecutor(), callback)
182                 awaitClose { telephonyManager.unregisterTelephonyCallback(callback) }
183             }
184             .scan(initial = initial) { state, event -> state.applyEvent(event) }
185             .stateIn(scope = scope, started = SharingStarted.WhileSubscribed(), initial)
186     }
187 
188     override val isEmergencyOnly =
189         callbackEvents
190             .mapNotNull { it.onServiceStateChanged }
191             .map { it.serviceState.isEmergencyOnly }
192             .stateIn(scope, SharingStarted.WhileSubscribed(), false)
193 
194     override val isRoaming =
195         callbackEvents
196             .mapNotNull { it.onServiceStateChanged }
197             .map { it.serviceState.roaming }
198             .stateIn(scope, SharingStarted.WhileSubscribed(), false)
199 
200     override val operatorAlphaShort =
201         callbackEvents
202             .mapNotNull { it.onServiceStateChanged }
203             .map { it.serviceState.operatorAlphaShort }
204             .stateIn(scope, SharingStarted.WhileSubscribed(), null)
205 
206     override val isInService =
207         callbackEvents
208             .mapNotNull { it.onServiceStateChanged }
209             .map { Utils.isInService(it.serviceState) }
210             .stateIn(scope, SharingStarted.WhileSubscribed(), false)
211 
212     override val isGsm =
213         callbackEvents
214             .mapNotNull { it.onSignalStrengthChanged }
215             .map { it.signalStrength.isGsm }
216             .stateIn(scope, SharingStarted.WhileSubscribed(), false)
217 
218     override val cdmaLevel =
219         callbackEvents
220             .mapNotNull { it.onSignalStrengthChanged }
221             .map {
222                 it.signalStrength.getCellSignalStrengths(CellSignalStrengthCdma::class.java).let {
223                     strengths ->
224                     if (strengths.isNotEmpty()) {
225                         strengths[0].level
226                     } else {
227                         SIGNAL_STRENGTH_NONE_OR_UNKNOWN
228                     }
229                 }
230             }
231             .stateIn(scope, SharingStarted.WhileSubscribed(), SIGNAL_STRENGTH_NONE_OR_UNKNOWN)
232 
233     override val primaryLevel =
234         callbackEvents
235             .mapNotNull { it.onSignalStrengthChanged }
236             .map { it.signalStrength.level }
237             .stateIn(scope, SharingStarted.WhileSubscribed(), SIGNAL_STRENGTH_NONE_OR_UNKNOWN)
238 
239     override val dataConnectionState =
240         callbackEvents
241             .mapNotNull { it.onDataConnectionStateChanged }
242             .map { it.dataState.toDataConnectionType() }
243             .stateIn(scope, SharingStarted.WhileSubscribed(), Disconnected)
244 
245     override val dataActivityDirection =
246         callbackEvents
247             .mapNotNull { it.onDataActivity }
248             .map { it.direction.toMobileDataActivityModel() }
249             .stateIn(
250                 scope,
251                 SharingStarted.WhileSubscribed(),
252                 DataActivityModel(hasActivityIn = false, hasActivityOut = false)
253             )
254 
255     override val carrierNetworkChangeActive =
256         callbackEvents
257             .mapNotNull { it.onCarrierNetworkChange }
258             .map { it.active }
259             .stateIn(scope, SharingStarted.WhileSubscribed(), false)
260 
261     override val resolvedNetworkType =
262         callbackEvents
263             .mapNotNull { it.onDisplayInfoChanged }
264             .map {
265                 if (it.telephonyDisplayInfo.overrideNetworkType != OVERRIDE_NETWORK_TYPE_NONE) {
266                     OverrideNetworkType(
267                         mobileMappingsProxy.toIconKeyOverride(
268                             it.telephonyDisplayInfo.overrideNetworkType
269                         )
270                     )
271                 } else if (it.telephonyDisplayInfo.networkType != NETWORK_TYPE_UNKNOWN) {
272                     DefaultNetworkType(
273                         mobileMappingsProxy.toIconKey(it.telephonyDisplayInfo.networkType)
274                     )
275                 } else {
276                     UnknownNetworkType
277                 }
278             }
279             .stateIn(scope, SharingStarted.WhileSubscribed(), UnknownNetworkType)
280 
281     override val numberOfLevels =
282         systemUiCarrierConfig.shouldInflateSignalStrength
283             .map { shouldInflate ->
284                 if (shouldInflate) {
285                     DEFAULT_NUM_LEVELS + 1
286                 } else {
287                     DEFAULT_NUM_LEVELS
288                 }
289             }
290             .stateIn(scope, SharingStarted.WhileSubscribed(), DEFAULT_NUM_LEVELS)
291 
292     override val carrierName =
293         subscriptionModel
294             .map {
295                 it?.let { model -> NetworkNameModel.SubscriptionDerived(model.carrierName) }
296                     ?: defaultNetworkName
297             }
298             .stateIn(scope, SharingStarted.WhileSubscribed(), defaultNetworkName)
299 
300     /**
301      * There are a few cases where we will need to poll [TelephonyManager] so we can update some
302      * internal state where callbacks aren't provided. Any of those events should be merged into
303      * this flow, which can be used to trigger the polling.
304      */
305     private val telephonyPollingEvent: Flow<Unit> = callbackEvents.map { Unit }
306 
307     override val cdmaRoaming: StateFlow<Boolean> =
308         telephonyPollingEvent
309             .mapLatest {
310                 val cdmaEri = telephonyManager.cdmaEnhancedRoamingIndicatorDisplayNumber
311                 cdmaEri == ERI_ON || cdmaEri == ERI_FLASH
312             }
313             .stateIn(scope, SharingStarted.WhileSubscribed(), false)
314 
315     override val carrierId =
316         broadcastDispatcher
317             .broadcastFlow(
318                 filter =
319                     IntentFilter(TelephonyManager.ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED),
320                 map = { intent, _ -> intent },
321             )
322             .filter { intent ->
323                 intent.getIntExtra(EXTRA_SUBSCRIPTION_ID, INVALID_SUBSCRIPTION_ID) == subId
324             }
325             .map { it.carrierId() }
326             .onStart {
327                 // Make sure we get the initial carrierId
328                 emit(telephonyManager.simCarrierId)
329             }
330             .stateIn(scope, SharingStarted.WhileSubscribed(), telephonyManager.simCarrierId)
331 
332     /** BroadcastDispatcher does not handle sticky broadcasts, so we can't use it here */
333     @SuppressLint("RegisterReceiverViaContext")
334     override val networkName: StateFlow<NetworkNameModel> =
335         conflatedCallbackFlow {
336                 val receiver =
337                     object : BroadcastReceiver() {
338                         override fun onReceive(context: Context, intent: Intent) {
339                             if (
340                                 intent.getIntExtra(
341                                     EXTRA_SUBSCRIPTION_INDEX,
342                                     INVALID_SUBSCRIPTION_ID
343                                 ) == subId
344                             ) {
345                                 logger.logServiceProvidersUpdatedBroadcast(intent)
346                                 trySend(
347                                     intent.toNetworkNameModel(networkNameSeparator)
348                                         ?: defaultNetworkName
349                                 )
350                             }
351                         }
352                     }
353 
354                 context.registerReceiver(
355                     receiver,
356                     IntentFilter(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED)
357                 )
358 
359                 awaitClose { context.unregisterReceiver(receiver) }
360             }
361             .stateIn(scope, SharingStarted.WhileSubscribed(), defaultNetworkName)
362 
363     override val dataEnabled = run {
364         val initial = telephonyManager.isDataConnectionAllowed
365         callbackEvents
366             .mapNotNull { it.onDataEnabledChanged }
367             .map { it.enabled }
368             .stateIn(scope, SharingStarted.WhileSubscribed(), initial)
369     }
370 
371     /** Typical mobile connections aren't available during airplane mode. */
372     override val isAllowedDuringAirplaneMode = MutableStateFlow(false).asStateFlow()
373 
374     class Factory
375     @Inject
376     constructor(
377         private val context: Context,
378         private val broadcastDispatcher: BroadcastDispatcher,
379         private val telephonyManager: TelephonyManager,
380         private val logger: MobileInputLogger,
381         private val carrierConfigRepository: CarrierConfigRepository,
382         private val mobileMappingsProxy: MobileMappingsProxy,
383         @Background private val bgDispatcher: CoroutineDispatcher,
384         @Application private val scope: CoroutineScope,
385     ) {
386         fun build(
387             subId: Int,
388             mobileLogger: TableLogBuffer,
389             subscriptionModel: StateFlow<SubscriptionModel?>,
390             defaultNetworkName: NetworkNameModel,
391             networkNameSeparator: String,
392         ): MobileConnectionRepository {
393             return MobileConnectionRepositoryImpl(
394                 subId,
395                 context,
396                 subscriptionModel,
397                 defaultNetworkName,
398                 networkNameSeparator,
399                 telephonyManager.createForSubscriptionId(subId),
400                 carrierConfigRepository.getOrCreateConfigForSubId(subId),
401                 broadcastDispatcher,
402                 mobileMappingsProxy,
403                 bgDispatcher,
404                 logger,
405                 mobileLogger,
406                 scope,
407             )
408         }
409     }
410 }
411 
Intentnull412 private fun Intent.carrierId(): Int =
413     getIntExtra(TelephonyManager.EXTRA_CARRIER_ID, UNKNOWN_CARRIER_ID)
414 
415 /**
416  * Wrap every [TelephonyCallback] we care about in a data class so we can accept them in a single
417  * shared flow and then split them back out into other flows.
418  */
419 sealed interface CallbackEvent {
420     data class OnCarrierNetworkChange(val active: Boolean) : CallbackEvent
421     data class OnDataActivity(val direction: Int) : CallbackEvent
422     data class OnDataConnectionStateChanged(val dataState: Int) : CallbackEvent
423     data class OnDataEnabledChanged(val enabled: Boolean) : CallbackEvent
424     data class OnDisplayInfoChanged(val telephonyDisplayInfo: TelephonyDisplayInfo) : CallbackEvent
425     data class OnServiceStateChanged(val serviceState: ServiceState) : CallbackEvent
426     data class OnSignalStrengthChanged(val signalStrength: SignalStrength) : CallbackEvent
427 }
428 
429 /**
430  * A simple box type for 1-to-1 mapping of [CallbackEvent] to the batched event. Used in conjunction
431  * with [scan] to make sure we don't drop important callbacks due to late subscribers
432  */
433 data class TelephonyCallbackState(
434     val onDataActivity: CallbackEvent.OnDataActivity? = null,
435     val onCarrierNetworkChange: CallbackEvent.OnCarrierNetworkChange? = null,
436     val onDataConnectionStateChanged: CallbackEvent.OnDataConnectionStateChanged? = null,
437     val onDataEnabledChanged: CallbackEvent.OnDataEnabledChanged? = null,
438     val onDisplayInfoChanged: CallbackEvent.OnDisplayInfoChanged? = null,
439     val onServiceStateChanged: CallbackEvent.OnServiceStateChanged? = null,
440     val onSignalStrengthChanged: CallbackEvent.OnSignalStrengthChanged? = null,
441 ) {
applyEventnull442     fun applyEvent(event: CallbackEvent): TelephonyCallbackState {
443         return when (event) {
444             is CallbackEvent.OnCarrierNetworkChange -> copy(onCarrierNetworkChange = event)
445             is CallbackEvent.OnDataActivity -> copy(onDataActivity = event)
446             is CallbackEvent.OnDataConnectionStateChanged ->
447                 copy(onDataConnectionStateChanged = event)
448             is CallbackEvent.OnDataEnabledChanged -> copy(onDataEnabledChanged = event)
449             is CallbackEvent.OnDisplayInfoChanged -> copy(onDisplayInfoChanged = event)
450             is CallbackEvent.OnServiceStateChanged -> {
451                 copy(onServiceStateChanged = event)
452             }
453             is CallbackEvent.OnSignalStrengthChanged -> copy(onSignalStrengthChanged = event)
454         }
455     }
456 }
457