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