1 /* 2 * 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 androidx.annotation.VisibleForTesting 20 import com.android.systemui.dagger.qualifiers.Application 21 import com.android.systemui.log.table.TableLogBuffer 22 import com.android.systemui.log.table.TableLogBufferFactory 23 import com.android.systemui.log.table.logDiffsForTable 24 import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel 25 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel 26 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository 27 import javax.inject.Inject 28 import kotlinx.coroutines.CoroutineScope 29 import kotlinx.coroutines.ExperimentalCoroutinesApi 30 import kotlinx.coroutines.flow.MutableStateFlow 31 import kotlinx.coroutines.flow.SharingStarted 32 import kotlinx.coroutines.flow.StateFlow 33 import kotlinx.coroutines.flow.flatMapLatest 34 import kotlinx.coroutines.flow.mapLatest 35 import kotlinx.coroutines.flow.stateIn 36 37 /** 38 * A repository that fully implements a mobile connection. 39 * 40 * This connection could either be a typical mobile connection (see [MobileConnectionRepositoryImpl] 41 * or a carrier merged connection (see [CarrierMergedConnectionRepository]). This repository 42 * switches between the two types of connections based on whether the connection is currently 43 * carrier merged (see [setIsCarrierMerged]). 44 */ 45 @Suppress("EXPERIMENTAL_IS_NOT_ENABLED") 46 @OptIn(ExperimentalCoroutinesApi::class) 47 class FullMobileConnectionRepository( 48 override val subId: Int, 49 startingIsCarrierMerged: Boolean, 50 override val tableLogBuffer: TableLogBuffer, 51 subscriptionModel: StateFlow<SubscriptionModel?>, 52 private val defaultNetworkName: NetworkNameModel, 53 private val networkNameSeparator: String, 54 @Application scope: CoroutineScope, 55 private val mobileRepoFactory: MobileConnectionRepositoryImpl.Factory, 56 private val carrierMergedRepoFactory: CarrierMergedConnectionRepository.Factory, 57 ) : MobileConnectionRepository { 58 /** 59 * Sets whether this connection is a typical mobile connection or a carrier merged connection. 60 */ 61 fun setIsCarrierMerged(isCarrierMerged: Boolean) { 62 _isCarrierMerged.value = isCarrierMerged 63 } 64 65 /** 66 * Returns true if this repo is currently for a carrier merged connection and false otherwise. 67 */ 68 @VisibleForTesting fun getIsCarrierMerged() = _isCarrierMerged.value 69 70 private val _isCarrierMerged = MutableStateFlow(startingIsCarrierMerged) 71 private val isCarrierMerged: StateFlow<Boolean> = 72 _isCarrierMerged 73 .logDiffsForTable( 74 tableLogBuffer, 75 columnPrefix = "", 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 columnPrefix = "", 131 columnName = COL_EMERGENCY, 132 activeRepo.value.isEmergencyOnly.value 133 ) 134 .stateIn( 135 scope, 136 SharingStarted.WhileSubscribed(), 137 activeRepo.value.isEmergencyOnly.value 138 ) 139 140 override val isRoaming = 141 activeRepo 142 .flatMapLatest { it.isRoaming } 143 .logDiffsForTable( 144 tableLogBuffer, 145 columnPrefix = "", 146 columnName = COL_ROAMING, 147 activeRepo.value.isRoaming.value 148 ) 149 .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.isRoaming.value) 150 151 override val operatorAlphaShort = 152 activeRepo 153 .flatMapLatest { it.operatorAlphaShort } 154 .logDiffsForTable( 155 tableLogBuffer, 156 columnPrefix = "", 157 columnName = COL_OPERATOR, 158 activeRepo.value.operatorAlphaShort.value 159 ) 160 .stateIn( 161 scope, 162 SharingStarted.WhileSubscribed(), 163 activeRepo.value.operatorAlphaShort.value 164 ) 165 166 override val isInService = 167 activeRepo 168 .flatMapLatest { it.isInService } 169 .logDiffsForTable( 170 tableLogBuffer, 171 columnPrefix = "", 172 columnName = COL_IS_IN_SERVICE, 173 activeRepo.value.isInService.value 174 ) 175 .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.isInService.value) 176 177 override val isGsm = 178 activeRepo 179 .flatMapLatest { it.isGsm } 180 .logDiffsForTable( 181 tableLogBuffer, 182 columnPrefix = "", 183 columnName = COL_IS_GSM, 184 activeRepo.value.isGsm.value 185 ) 186 .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.isGsm.value) 187 188 override val cdmaLevel = 189 activeRepo 190 .flatMapLatest { it.cdmaLevel } 191 .logDiffsForTable( 192 tableLogBuffer, 193 columnPrefix = "", 194 columnName = COL_CDMA_LEVEL, 195 activeRepo.value.cdmaLevel.value 196 ) 197 .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.cdmaLevel.value) 198 199 override val primaryLevel = 200 activeRepo 201 .flatMapLatest { it.primaryLevel } 202 .logDiffsForTable( 203 tableLogBuffer, 204 columnPrefix = "", 205 columnName = COL_PRIMARY_LEVEL, 206 activeRepo.value.primaryLevel.value 207 ) 208 .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.primaryLevel.value) 209 210 override val dataConnectionState = 211 activeRepo 212 .flatMapLatest { it.dataConnectionState } 213 .logDiffsForTable( 214 tableLogBuffer, 215 columnPrefix = "", 216 activeRepo.value.dataConnectionState.value 217 ) 218 .stateIn( 219 scope, 220 SharingStarted.WhileSubscribed(), 221 activeRepo.value.dataConnectionState.value 222 ) 223 224 override val dataActivityDirection = 225 activeRepo 226 .flatMapLatest { it.dataActivityDirection } 227 .logDiffsForTable( 228 tableLogBuffer, 229 columnPrefix = "", 230 activeRepo.value.dataActivityDirection.value 231 ) 232 .stateIn( 233 scope, 234 SharingStarted.WhileSubscribed(), 235 activeRepo.value.dataActivityDirection.value 236 ) 237 238 override val carrierNetworkChangeActive = 239 activeRepo 240 .flatMapLatest { it.carrierNetworkChangeActive } 241 .logDiffsForTable( 242 tableLogBuffer, 243 columnPrefix = "", 244 columnName = COL_CARRIER_NETWORK_CHANGE, 245 activeRepo.value.carrierNetworkChangeActive.value 246 ) 247 .stateIn( 248 scope, 249 SharingStarted.WhileSubscribed(), 250 activeRepo.value.carrierNetworkChangeActive.value 251 ) 252 253 override val resolvedNetworkType = 254 activeRepo 255 .flatMapLatest { it.resolvedNetworkType } 256 .logDiffsForTable( 257 tableLogBuffer, 258 columnPrefix = "", 259 activeRepo.value.resolvedNetworkType.value 260 ) 261 .stateIn( 262 scope, 263 SharingStarted.WhileSubscribed(), 264 activeRepo.value.resolvedNetworkType.value 265 ) 266 267 override val dataEnabled = 268 activeRepo 269 .flatMapLatest { it.dataEnabled } 270 .logDiffsForTable( 271 tableLogBuffer, 272 columnPrefix = "", 273 columnName = "dataEnabled", 274 initialValue = activeRepo.value.dataEnabled.value, 275 ) 276 .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.dataEnabled.value) 277 278 override val numberOfLevels = 279 activeRepo 280 .flatMapLatest { it.numberOfLevels } 281 .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.numberOfLevels.value) 282 283 override val networkName = 284 activeRepo 285 .flatMapLatest { it.networkName } 286 .logDiffsForTable( 287 tableLogBuffer, 288 columnPrefix = "", 289 initialValue = activeRepo.value.networkName.value, 290 ) 291 .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.networkName.value) 292 293 override val carrierName = 294 activeRepo 295 .flatMapLatest { it.carrierName } 296 .logDiffsForTable( 297 tableLogBuffer, 298 columnPrefix = "", 299 initialValue = activeRepo.value.carrierName.value, 300 ) 301 .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.carrierName.value) 302 303 override val isAllowedDuringAirplaneMode = 304 activeRepo 305 .flatMapLatest { it.isAllowedDuringAirplaneMode } 306 .stateIn( 307 scope, 308 SharingStarted.WhileSubscribed(), 309 activeRepo.value.isAllowedDuringAirplaneMode.value, 310 ) 311 312 class Factory 313 @Inject 314 constructor( 315 @Application private val scope: CoroutineScope, 316 private val logFactory: TableLogBufferFactory, 317 private val mobileRepoFactory: MobileConnectionRepositoryImpl.Factory, 318 private val carrierMergedRepoFactory: CarrierMergedConnectionRepository.Factory, 319 ) { 320 fun build( 321 subId: Int, 322 startingIsCarrierMerged: Boolean, 323 subscriptionModel: StateFlow<SubscriptionModel?>, 324 defaultNetworkName: NetworkNameModel, 325 networkNameSeparator: String, 326 ): FullMobileConnectionRepository { 327 val mobileLogger = 328 logFactory.getOrCreate(tableBufferLogName(subId), MOBILE_CONNECTION_BUFFER_SIZE) 329 330 return FullMobileConnectionRepository( 331 subId, 332 startingIsCarrierMerged, 333 mobileLogger, 334 subscriptionModel, 335 defaultNetworkName, 336 networkNameSeparator, 337 scope, 338 mobileRepoFactory, 339 carrierMergedRepoFactory, 340 ) 341 } 342 343 companion object { 344 /** The buffer size to use for logging. */ 345 const val MOBILE_CONNECTION_BUFFER_SIZE = 100 346 347 /** Returns a log buffer name for a mobile connection with the given [subId]. */ 348 fun tableBufferLogName(subId: Int): String = "MobileConnectionLog[$subId]" 349 } 350 } 351 352 companion object { 353 const val COL_CARRIER_ID = "carrierId" 354 const val COL_CARRIER_NETWORK_CHANGE = "carrierNetworkChangeActive" 355 const val COL_CDMA_LEVEL = "cdmaLevel" 356 const val COL_EMERGENCY = "emergencyOnly" 357 const val COL_IS_GSM = "isGsm" 358 const val COL_IS_IN_SERVICE = "isInService" 359 const val COL_OPERATOR = "operatorName" 360 const val COL_PRIMARY_LEVEL = "primaryLevel" 361 const val COL_ROAMING = "roaming" 362 } 363 } 364