• 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.telephony.CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN
20 import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
21 import android.telephony.TelephonyManager
22 import android.util.Log
23 import com.android.systemui.dagger.SysUISingleton
24 import com.android.systemui.dagger.qualifiers.Background
25 import com.android.systemui.log.table.TableLogBuffer
26 import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
27 import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
28 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType
29 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
30 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
31 import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
32 import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
33 import javax.inject.Inject
34 import kotlin.coroutines.CoroutineContext
35 import kotlinx.coroutines.CoroutineScope
36 import kotlinx.coroutines.flow.Flow
37 import kotlinx.coroutines.flow.MutableStateFlow
38 import kotlinx.coroutines.flow.SharingStarted
39 import kotlinx.coroutines.flow.StateFlow
40 import kotlinx.coroutines.flow.asStateFlow
41 import kotlinx.coroutines.flow.combine
42 import kotlinx.coroutines.flow.map
43 import kotlinx.coroutines.flow.stateIn
44 import kotlinx.coroutines.withContext
45 
46 /**
47  * A repository implementation for a carrier merged (aka VCN) network. A carrier merged network is
48  * delivered to SysUI as a wifi network (see [WifiNetworkModel.CarrierMerged], but is visually
49  * displayed as a mobile network triangle.
50  *
51  * See [android.net.wifi.WifiInfo.isCarrierMerged] for more information.
52  *
53  * See [MobileConnectionRepositoryImpl] for a repository implementation of a typical mobile
54  * connection.
55  */
56 class CarrierMergedConnectionRepository(
57     override val subId: Int,
58     override val tableLogBuffer: TableLogBuffer,
59     private val telephonyManager: TelephonyManager,
60     private val bgContext: CoroutineContext,
61     @Background private val scope: CoroutineScope,
62     val wifiRepository: WifiRepository,
63 ) : MobileConnectionRepository {
64     init {
65         if (telephonyManager.subscriptionId != subId) {
66             throw IllegalStateException(
67                 "CarrierMergedRepo: TelephonyManager should be created with subId($subId). " +
68                     "Found ${telephonyManager.subscriptionId} instead."
69             )
70         }
71     }
72 
73     /**
74      * Outputs the carrier merged network to use, or null if we don't have a valid carrier merged
75      * network.
76      */
77     private val network: Flow<WifiNetworkModel.CarrierMerged?> =
78         combine(
79             wifiRepository.isWifiEnabled,
80             wifiRepository.isWifiDefault,
81             wifiRepository.wifiNetwork,
82         ) { isEnabled, isDefault, network ->
83             when {
84                 !isEnabled -> null
85                 !isDefault -> null
86                 network !is WifiNetworkModel.CarrierMerged -> null
87                 network.subscriptionId != subId -> {
88                     Log.w(
89                         TAG,
90                         "Connection repo subId=$subId " +
91                             "does not equal wifi repo subId=${network.subscriptionId}; " +
92                             "not showing carrier merged",
93                     )
94                     null
95                 }
96                 else -> network
97             }
98         }
99 
100     override val cdmaRoaming: StateFlow<Boolean> = MutableStateFlow(ROAMING).asStateFlow()
101 
102     override val networkName: StateFlow<NetworkNameModel> =
103         network
104             // The SIM operator name should be the same throughout the lifetime of a subId, **but**
105             // it may not be available when this repo is created because it takes time to load. To
106             // be safe, we re-fetch it each time the network has changed.
107             .map { NetworkNameModel.SimDerived(telephonyManager.simOperatorName) }
108             .stateIn(
109                 scope,
110                 SharingStarted.WhileSubscribed(),
111                 NetworkNameModel.SimDerived(telephonyManager.simOperatorName),
112             )
113 
114     override val carrierName: StateFlow<NetworkNameModel> = networkName
115 
116     override val numberOfLevels: StateFlow<Int> =
117         wifiRepository.wifiNetwork
118             .map {
119                 if (it is WifiNetworkModel.CarrierMerged) {
120                     it.numberOfLevels
121                 } else {
122                     DEFAULT_NUM_LEVELS
123                 }
124             }
125             .stateIn(scope, SharingStarted.WhileSubscribed(), DEFAULT_NUM_LEVELS)
126 
127     override val primaryLevel =
128         network
129             .map { it?.level ?: SIGNAL_STRENGTH_NONE_OR_UNKNOWN }
130             .stateIn(scope, SharingStarted.WhileSubscribed(), SIGNAL_STRENGTH_NONE_OR_UNKNOWN)
131 
132     override val cdmaLevel =
133         network
134             .map { it?.level ?: SIGNAL_STRENGTH_NONE_OR_UNKNOWN }
135             .stateIn(scope, SharingStarted.WhileSubscribed(), SIGNAL_STRENGTH_NONE_OR_UNKNOWN)
136 
137     override val dataActivityDirection = wifiRepository.wifiActivity
138 
139     override val resolvedNetworkType =
140         network
141             .map {
142                 if (it != null) {
143                     ResolvedNetworkType.CarrierMergedNetworkType
144                 } else {
145                     ResolvedNetworkType.UnknownNetworkType
146                 }
147             }
148             .stateIn(
149                 scope,
150                 SharingStarted.WhileSubscribed(),
151                 ResolvedNetworkType.UnknownNetworkType,
152             )
153 
154     override val dataConnectionState =
155         network
156             .map {
157                 if (it != null) {
158                     DataConnectionState.Connected
159                 } else {
160                     DataConnectionState.Disconnected
161                 }
162             }
163             .stateIn(scope, SharingStarted.WhileSubscribed(), DataConnectionState.Disconnected)
164 
165     override val isRoaming = MutableStateFlow(false).asStateFlow()
166     override val carrierId = MutableStateFlow(INVALID_SUBSCRIPTION_ID).asStateFlow()
167     override val inflateSignalStrength = MutableStateFlow(false).asStateFlow()
168     override val allowNetworkSliceIndicator = MutableStateFlow(false).asStateFlow()
169     override val isEmergencyOnly = MutableStateFlow(false).asStateFlow()
170     override val operatorAlphaShort = MutableStateFlow(null).asStateFlow()
171     override val isInService = MutableStateFlow(true).asStateFlow()
172     override val isNonTerrestrial = MutableStateFlow(false).asStateFlow()
173     override val isGsm = MutableStateFlow(false).asStateFlow()
174     override val carrierNetworkChangeActive = MutableStateFlow(false).asStateFlow()
175     override val satelliteLevel = MutableStateFlow(0)
176 
177     /**
178      * Carrier merged connections happen over wifi but are displayed as a mobile triangle. Because
179      * they occur over wifi, it's possible to have a valid carrier merged connection even during
180      * airplane mode. See b/291993542.
181      */
182     override val isAllowedDuringAirplaneMode = MutableStateFlow(true).asStateFlow()
183 
184     /**
185      * It's not currently considered possible that a carrier merged network can have these
186      * prioritized capabilities. If we need to track them, we can add the same check as is in
187      * [MobileConnectionRepositoryImpl].
188      */
189     override val hasPrioritizedNetworkCapabilities = MutableStateFlow(false).asStateFlow()
190 
191     override val dataEnabled: StateFlow<Boolean> = wifiRepository.isWifiEnabled
192 
193     override suspend fun isInEcmMode(): Boolean =
194         withContext(bgContext) { telephonyManager.emergencyCallbackMode }
195 
196     companion object {
197         // Carrier merged is never roaming
198         private const val ROAMING = false
199     }
200 
201     @SysUISingleton
202     class Factory
203     @Inject
204     constructor(
205         private val telephonyManager: TelephonyManager,
206         @Background private val bgContext: CoroutineContext,
207         @Background private val scope: CoroutineScope,
208         private val wifiRepository: WifiRepository,
209     ) {
210         fun build(subId: Int, mobileLogger: TableLogBuffer): MobileConnectionRepository {
211             return CarrierMergedConnectionRepository(
212                 subId,
213                 mobileLogger,
214                 telephonyManager.createForSubscriptionId(subId),
215                 bgContext,
216                 scope,
217                 wifiRepository,
218             )
219         }
220     }
221 }
222 
223 private const val TAG = "CarrierMergedConnectionRepository"
224