Merge changes I6843e949,Ied9eea84,Id3ae744f into tm-qpr-dev

* changes:
  [Sb refactor] Rework TelephonyCallback flows
  [Sb refactor] Implement DataEnabledChangedListener
  [Sb refactor] CarrierConfigRepository
This commit is contained in:
Evan Laird
2023-02-07 18:27:36 +00:00
committed by Android (Google) Code Review
20 changed files with 794 additions and 248 deletions

View File

@@ -49,6 +49,7 @@
<permission name="android.permission.READ_FRAME_BUFFER"/> <permission name="android.permission.READ_FRAME_BUFFER"/>
<permission name="android.permission.READ_NETWORK_USAGE_HISTORY"/> <permission name="android.permission.READ_NETWORK_USAGE_HISTORY"/>
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/> <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
<permission name="android.permission.READ_PRECISE_PHONE_STATE"/>
<permission name="android.permission.REAL_GET_TASKS"/> <permission name="android.permission.REAL_GET_TASKS"/>
<permission name="android.permission.REQUEST_NETWORK_SCORES"/> <permission name="android.permission.REQUEST_NETWORK_SCORES"/>
<permission name="android.permission.RECEIVE_MEDIA_RESOURCE_USAGE"/> <permission name="android.permission.RECEIVE_MEDIA_RESOURCE_USAGE"/>

View File

@@ -68,6 +68,7 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" /> <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_PRECISE_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.OVERRIDE_WIFI_CONFIG" /> <uses-permission android:name="android.permission.OVERRIDE_WIFI_CONFIG" />

View File

@@ -25,6 +25,7 @@ import com.android.systemui.statusbar.pipeline.airplane.data.repository.Airplane
import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepositoryImpl import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepositoryImpl
import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModelImpl import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModelImpl
import com.android.systemui.statusbar.pipeline.mobile.data.repository.CarrierConfigCoreStartable
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileRepositorySwitcher import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileRepositorySwitcher
import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupRepository import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupRepository
@@ -82,6 +83,11 @@ abstract class StatusBarPipelineModule {
@ClassKey(MobileUiAdapter::class) @ClassKey(MobileUiAdapter::class)
abstract fun bindFeature(impl: MobileUiAdapter): CoreStartable abstract fun bindFeature(impl: MobileUiAdapter): CoreStartable
@Binds
@IntoMap
@ClassKey(CarrierConfigCoreStartable::class)
abstract fun bindCarrierConfigStartable(impl: CarrierConfigCoreStartable): CoreStartable
companion object { companion object {
@Provides @Provides
@SysUISingleton @SysUISingleton

View File

@@ -0,0 +1,108 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.statusbar.pipeline.mobile.data.model
import android.os.PersistableBundle
import android.telephony.CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL
import android.telephony.CarrierConfigManager.KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL
import androidx.annotation.VisibleForTesting
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
/**
* Represents, for a given subscription ID, the set of keys about which SystemUI cares.
*
* Upon first creation, this config represents only the default configuration (see
* [android.telephony.CarrierConfigManager.getDefaultConfig]).
*
* Upon request (see
* [com.android.systemui.statusbar.pipeline.mobile.data.repository.CarrierConfigRepository]), an
* instance of this class may be created for a given subscription Id, and will default to
* representing the default carrier configuration. However, once a carrier config is received for
* this [subId], all fields will reflect those in the received config, using [PersistableBundle]'s
* default of false for any config that is not present in the override.
*
* To keep things relatively simple, this class defines a wrapper around each config key which
* exposes a StateFlow<Boolean> for each config we care about. It also tracks whether or not it is
* using the default config for logging purposes.
*
* NOTE to add new keys to be tracked:
* 1. Define a new `private val` wrapping the key using [BooleanCarrierConfig]
* 2. Define a public `val` exposing the wrapped flow using [BooleanCarrierConfig.config]
* 3. Add the new [BooleanCarrierConfig] to the list of tracked configs, so they are properly
* updated when a new carrier config comes down
*/
class SystemUiCarrierConfig
internal constructor(
val subId: Int,
defaultConfig: PersistableBundle,
) {
@VisibleForTesting
var isUsingDefault = true
private set
private val inflateSignalStrength =
BooleanCarrierConfig(KEY_INFLATE_SIGNAL_STRENGTH_BOOL, defaultConfig)
/** Flow tracking the [KEY_INFLATE_SIGNAL_STRENGTH_BOOL] carrier config */
val shouldInflateSignalStrength: StateFlow<Boolean> = inflateSignalStrength.config
private val showOperatorName =
BooleanCarrierConfig(KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL, defaultConfig)
/** Flow tracking the [KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL] config */
val showOperatorNameInStatusBar: StateFlow<Boolean> = showOperatorName.config
private val trackedConfigs =
listOf(
inflateSignalStrength,
showOperatorName,
)
/** Ingest a new carrier config, and switch all of the tracked keys over to the new values */
fun processNewCarrierConfig(config: PersistableBundle) {
isUsingDefault = false
trackedConfigs.forEach { it.update(config) }
}
/** For dumpsys, shortcut if we haven't overridden any keys */
fun toStringConsideringDefaults(): String {
return if (isUsingDefault) {
"using defaults"
} else {
trackedConfigs.joinToString { it.toString() }
}
}
override fun toString(): String = trackedConfigs.joinToString { it.toString() }
}
/** Extracts [key] from the carrier config, and stores it in a flow */
private class BooleanCarrierConfig(
val key: String,
defaultConfig: PersistableBundle,
) {
private val _configValue = MutableStateFlow(defaultConfig.getBoolean(key))
val config = _configValue.asStateFlow()
fun update(config: PersistableBundle) {
_configValue.value = config.getBoolean(key)
}
override fun toString(): String {
return "$key=${config.value}"
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.statusbar.pipeline.mobile.data.repository
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.qualifiers.Application
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
/**
* Core startable which configures the [CarrierConfigRepository] to listen for updates for the
* lifetime of the process
*/
class CarrierConfigCoreStartable
@Inject
constructor(
private val carrierConfigRepository: CarrierConfigRepository,
@Application private val scope: CoroutineScope,
) : CoreStartable {
override fun start() {
scope.launch { carrierConfigRepository.startObservingCarrierConfigUpdates() }
}
}

View File

@@ -0,0 +1,136 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.statusbar.pipeline.mobile.data.repository
import android.content.IntentFilter
import android.os.PersistableBundle
import android.telephony.CarrierConfigManager
import android.telephony.SubscriptionManager
import android.util.SparseArray
import androidx.annotation.VisibleForTesting
import androidx.core.util.getOrElse
import androidx.core.util.isEmpty
import androidx.core.util.keyIterator
import com.android.systemui.Dumpable
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import java.io.PrintWriter
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.shareIn
/**
* Meant to be the source of truth regarding CarrierConfigs. These are configuration objects defined
* on a per-subscriptionId basis, and do not trigger a device configuration event.
*
* Designed to supplant [com.android.systemui.util.CarrierConfigTracker].
*
* See [SystemUiCarrierConfig] for details on how to add carrier config keys to be tracked
*/
@SysUISingleton
class CarrierConfigRepository
@Inject
constructor(
broadcastDispatcher: BroadcastDispatcher,
private val carrierConfigManager: CarrierConfigManager,
dumpManager: DumpManager,
logger: ConnectivityPipelineLogger,
@Application scope: CoroutineScope,
) : Dumpable {
private var isListening = false
private val defaultConfig: PersistableBundle by lazy { CarrierConfigManager.getDefaultConfig() }
// Used for logging the default config in the dumpsys
private val defaultConfigForLogs: SystemUiCarrierConfig by lazy {
SystemUiCarrierConfig(-1, defaultConfig)
}
private val configs = SparseArray<SystemUiCarrierConfig>()
init {
dumpManager.registerNormalDumpable(this)
}
@VisibleForTesting
val carrierConfigStream: SharedFlow<Pair<Int, PersistableBundle>> =
broadcastDispatcher
.broadcastFlow(IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
intent,
_ ->
intent.getIntExtra(
CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX,
SubscriptionManager.INVALID_SUBSCRIPTION_ID
)
}
.onEach { logger.logCarrierConfigChanged(it) }
.filter { SubscriptionManager.isValidSubscriptionId(it) }
.mapNotNull { subId ->
val config = carrierConfigManager.getConfigForSubId(subId)
config?.let { subId to it }
}
.shareIn(scope, SharingStarted.WhileSubscribed())
/**
* Start this repository observing broadcasts for **all** carrier configuration updates. Must be
* called in order to keep SystemUI in sync with [CarrierConfigManager].
*/
suspend fun startObservingCarrierConfigUpdates() {
isListening = true
carrierConfigStream.collect { updateCarrierConfig(it.first, it.second) }
}
/** Update or create the [SystemUiCarrierConfig] for subId with the override */
private fun updateCarrierConfig(subId: Int, config: PersistableBundle) {
val configToUpdate = getOrCreateConfigForSubId(subId)
configToUpdate.processNewCarrierConfig(config)
}
/** Gets a cached [SystemUiCarrierConfig], or creates a new one which will track the defaults */
fun getOrCreateConfigForSubId(subId: Int): SystemUiCarrierConfig {
return configs.getOrElse(subId) {
val config = SystemUiCarrierConfig(subId, defaultConfig)
val carrierConfig = carrierConfigManager.getConfigForSubId(subId)
if (carrierConfig != null) config.processNewCarrierConfig(carrierConfig)
configs.put(subId, config)
config
}
}
override fun dump(pw: PrintWriter, args: Array<out String>) {
pw.println("isListening: $isListening")
if (configs.isEmpty()) {
pw.println("no carrier configs loaded")
} else {
pw.println("Carrier configs by subId")
configs.keyIterator().forEach {
pw.println(" subId=$it")
pw.println(" config=${configs.get(it).toStringConsideringDefaults()}")
}
// Finally, print the default config
pw.println("Default config:")
pw.println(" $defaultConfigForLogs")
}
}
}

View File

@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.pipeline.mobile.data.repository package com.android.systemui.statusbar.pipeline.mobile.data.repository
import android.provider.Settings
import android.telephony.CarrierConfigManager import android.telephony.CarrierConfigManager
import android.telephony.SubscriptionManager import android.telephony.SubscriptionManager
import com.android.settingslib.SignalIcon.MobileIconGroup import com.android.settingslib.SignalIcon.MobileIconGroup
@@ -53,9 +52,6 @@ interface MobileConnectionsRepository {
/** Get or create a repository for the line of service for the given subscription ID */ /** Get or create a repository for the line of service for the given subscription ID */
fun getRepoForSubId(subId: Int): MobileConnectionRepository fun getRepoForSubId(subId: Int): MobileConnectionRepository
/** Observe changes to the [Settings.Global.MOBILE_DATA] setting */
val globalMobileDataSettingChangedEvent: Flow<Unit>
/** /**
* [Config] is an object that tracks relevant configuration flags for a given subscription ID. * [Config] is an object that tracks relevant configuration flags for a given subscription ID.
* In the case of [MobileMappings], it's hard-coded to check the default data subscription's * In the case of [MobileMappings], it's hard-coded to check the default data subscription's

View File

@@ -156,9 +156,6 @@ constructor(
realRepository.defaultMobileNetworkConnectivity.value realRepository.defaultMobileNetworkConnectivity.value
) )
override val globalMobileDataSettingChangedEvent: Flow<Unit> =
activeRepo.flatMapLatest { it.globalMobileDataSettingChangedEvent }
override fun getRepoForSubId(subId: Int): MobileConnectionRepository { override fun getRepoForSubId(subId: Int): MobileConnectionRepository {
if (isDemoMode.value) { if (isDemoMode.value) {
return demoMobileConnectionsRepository.getRepoForSubId(subId) return demoMobileConnectionsRepository.getRepoForSubId(subId)

View File

@@ -185,8 +185,6 @@ constructor(
return CacheContainer(repo, lastMobileState = null) return CacheContainer(repo, lastMobileState = null)
} }
override val globalMobileDataSettingChangedEvent = MutableStateFlow(Unit)
fun startProcessingCommands() { fun startProcessingCommands() {
mobileDemoCommandJob = mobileDemoCommandJob =
scope.launch { scope.launch {

View File

@@ -26,7 +26,6 @@ import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConn
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
@@ -50,7 +49,6 @@ class FullMobileConnectionRepository(
override val tableLogBuffer: TableLogBuffer, override val tableLogBuffer: TableLogBuffer,
private val defaultNetworkName: NetworkNameModel, private val defaultNetworkName: NetworkNameModel,
private val networkNameSeparator: String, private val networkNameSeparator: String,
private val globalMobileDataSettingChangedEvent: Flow<Unit>,
@Application scope: CoroutineScope, @Application scope: CoroutineScope,
private val mobileRepoFactory: MobileConnectionRepositoryImpl.Factory, private val mobileRepoFactory: MobileConnectionRepositoryImpl.Factory,
private val carrierMergedRepoFactory: CarrierMergedConnectionRepository.Factory, private val carrierMergedRepoFactory: CarrierMergedConnectionRepository.Factory,
@@ -84,7 +82,6 @@ class FullMobileConnectionRepository(
tableLogBuffer, tableLogBuffer,
defaultNetworkName, defaultNetworkName,
networkNameSeparator, networkNameSeparator,
globalMobileDataSettingChangedEvent,
) )
} }
@@ -150,7 +147,6 @@ class FullMobileConnectionRepository(
startingIsCarrierMerged: Boolean, startingIsCarrierMerged: Boolean,
defaultNetworkName: NetworkNameModel, defaultNetworkName: NetworkNameModel,
networkNameSeparator: String, networkNameSeparator: String,
globalMobileDataSettingChangedEvent: Flow<Unit>,
): FullMobileConnectionRepository { ): FullMobileConnectionRepository {
val mobileLogger = val mobileLogger =
logFactory.getOrCreate(tableBufferLogName(subId), MOBILE_CONNECTION_BUFFER_SIZE) logFactory.getOrCreate(tableBufferLogName(subId), MOBILE_CONNECTION_BUFFER_SIZE)
@@ -161,7 +157,6 @@ class FullMobileConnectionRepository(
mobileLogger, mobileLogger,
defaultNetworkName, defaultNetworkName,
networkNameSeparator, networkNameSeparator,
globalMobileDataSettingChangedEvent,
scope, scope,
mobileRepoFactory, mobileRepoFactory,
carrierMergedRepoFactory, carrierMergedRepoFactory,

View File

@@ -18,8 +18,6 @@ package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod
import android.content.Context import android.content.Context
import android.content.IntentFilter import android.content.IntentFilter
import android.database.ContentObserver
import android.provider.Settings.Global
import android.telephony.CellSignalStrength import android.telephony.CellSignalStrength
import android.telephony.CellSignalStrengthCdma import android.telephony.CellSignalStrengthCdma
import android.telephony.ServiceState import android.telephony.ServiceState
@@ -44,14 +42,15 @@ import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameMode
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.DefaultNetworkType import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.DefaultNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.OverrideNetworkType import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.OverrideNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.UnknownNetworkType import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.UnknownNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig
import com.android.systemui.statusbar.pipeline.mobile.data.model.toDataConnectionType import com.android.systemui.statusbar.pipeline.mobile.data.model.toDataConnectionType
import com.android.systemui.statusbar.pipeline.mobile.data.model.toNetworkNameModel import com.android.systemui.statusbar.pipeline.mobile.data.model.toNetworkNameModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.CarrierConfigRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
import com.android.systemui.util.settings.GlobalSettings
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@@ -60,13 +59,15 @@ import kotlinx.coroutines.asExecutor
import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.scan
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
/** /**
@@ -81,10 +82,9 @@ class MobileConnectionRepositoryImpl(
defaultNetworkName: NetworkNameModel, defaultNetworkName: NetworkNameModel,
networkNameSeparator: String, networkNameSeparator: String,
private val telephonyManager: TelephonyManager, private val telephonyManager: TelephonyManager,
private val globalSettings: GlobalSettings, systemUiCarrierConfig: SystemUiCarrierConfig,
broadcastDispatcher: BroadcastDispatcher, broadcastDispatcher: BroadcastDispatcher,
globalMobileDataSettingChangedEvent: Flow<Unit>, private val mobileMappingsProxy: MobileMappingsProxy,
mobileMappingsProxy: MobileMappingsProxy,
bgDispatcher: CoroutineDispatcher, bgDispatcher: CoroutineDispatcher,
logger: ConnectivityPipelineLogger, logger: ConnectivityPipelineLogger,
mobileLogger: TableLogBuffer, mobileLogger: TableLogBuffer,
@@ -103,8 +103,15 @@ class MobileConnectionRepositoryImpl(
override val tableLogBuffer: TableLogBuffer = mobileLogger override val tableLogBuffer: TableLogBuffer = mobileLogger
override val connectionInfo: StateFlow<MobileConnectionModel> = run { /**
var state = MobileConnectionModel() * This flow defines the single shared connection to system_server via TelephonyCallback. Any
* new callback should be added to this listener and funneled through callbackEvents via a data
* class. See [CallbackEvent] for defining new callbacks.
*
* The reason we need to do this is because TelephonyManager limits the number of registered
* listeners per-process, so we don't want to create a new listener for every callback.
*/
private val callbackEvents: SharedFlow<CallbackEvent> =
conflatedCallbackFlow { conflatedCallbackFlow {
val callback = val callback =
object : object :
@@ -114,41 +121,16 @@ class MobileConnectionRepositoryImpl(
TelephonyCallback.DataConnectionStateListener, TelephonyCallback.DataConnectionStateListener,
TelephonyCallback.DataActivityListener, TelephonyCallback.DataActivityListener,
TelephonyCallback.CarrierNetworkListener, TelephonyCallback.CarrierNetworkListener,
TelephonyCallback.DisplayInfoListener { TelephonyCallback.DisplayInfoListener,
TelephonyCallback.DataEnabledListener {
override fun onServiceStateChanged(serviceState: ServiceState) { override fun onServiceStateChanged(serviceState: ServiceState) {
logger.logOnServiceStateChanged(serviceState, subId) logger.logOnServiceStateChanged(serviceState, subId)
state = trySend(CallbackEvent.OnServiceStateChanged(serviceState))
state.copy(
isEmergencyOnly = serviceState.isEmergencyOnly,
isRoaming = serviceState.roaming,
operatorAlphaShort = serviceState.operatorAlphaShort,
isInService = Utils.isInService(serviceState),
)
trySend(state)
} }
override fun onSignalStrengthsChanged(signalStrength: SignalStrength) { override fun onSignalStrengthsChanged(signalStrength: SignalStrength) {
logger.logOnSignalStrengthsChanged(signalStrength, subId) logger.logOnSignalStrengthsChanged(signalStrength, subId)
val cdmaLevel = trySend(CallbackEvent.OnSignalStrengthChanged(signalStrength))
signalStrength
.getCellSignalStrengths(CellSignalStrengthCdma::class.java)
.let { strengths ->
if (!strengths.isEmpty()) {
strengths[0].level
} else {
CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN
}
}
val primaryLevel = signalStrength.level
state =
state.copy(
cdmaLevel = cdmaLevel,
primaryLevel = primaryLevel,
isGsm = signalStrength.isGsm,
)
trySend(state)
} }
override fun onDataConnectionStateChanged( override fun onDataConnectionStateChanged(
@@ -156,101 +138,136 @@ class MobileConnectionRepositoryImpl(
networkType: Int networkType: Int
) { ) {
logger.logOnDataConnectionStateChanged(dataState, networkType, subId) logger.logOnDataConnectionStateChanged(dataState, networkType, subId)
state = trySend(CallbackEvent.OnDataConnectionStateChanged(dataState))
state.copy(dataConnectionState = dataState.toDataConnectionType())
trySend(state)
} }
override fun onDataActivity(direction: Int) { override fun onDataActivity(direction: Int) {
logger.logOnDataActivity(direction, subId) logger.logOnDataActivity(direction, subId)
state = trySend(CallbackEvent.OnDataActivity(direction))
state.copy(
dataActivityDirection = direction.toMobileDataActivityModel()
)
trySend(state)
} }
override fun onCarrierNetworkChange(active: Boolean) { override fun onCarrierNetworkChange(active: Boolean) {
logger.logOnCarrierNetworkChange(active, subId) logger.logOnCarrierNetworkChange(active, subId)
state = state.copy(carrierNetworkChangeActive = active) trySend(CallbackEvent.OnCarrierNetworkChange(active))
trySend(state)
} }
override fun onDisplayInfoChanged( override fun onDisplayInfoChanged(
telephonyDisplayInfo: TelephonyDisplayInfo telephonyDisplayInfo: TelephonyDisplayInfo
) { ) {
logger.logOnDisplayInfoChanged(telephonyDisplayInfo, subId) logger.logOnDisplayInfoChanged(telephonyDisplayInfo, subId)
trySend(CallbackEvent.OnDisplayInfoChanged(telephonyDisplayInfo))
}
val networkType = override fun onDataEnabledChanged(enabled: Boolean, reason: Int) {
if (telephonyDisplayInfo.networkType == NETWORK_TYPE_UNKNOWN) { logger.logOnDataEnabledChanged(enabled, subId)
UnknownNetworkType trySend(CallbackEvent.OnDataEnabledChanged(enabled))
} else if (
telephonyDisplayInfo.overrideNetworkType ==
OVERRIDE_NETWORK_TYPE_NONE
) {
DefaultNetworkType(
mobileMappingsProxy.toIconKey(
telephonyDisplayInfo.networkType
)
)
} else {
OverrideNetworkType(
mobileMappingsProxy.toIconKeyOverride(
telephonyDisplayInfo.overrideNetworkType
)
)
}
state = state.copy(resolvedNetworkType = networkType)
trySend(state)
} }
} }
telephonyManager.registerTelephonyCallback(bgDispatcher.asExecutor(), callback) telephonyManager.registerTelephonyCallback(bgDispatcher.asExecutor(), callback)
awaitClose { telephonyManager.unregisterTelephonyCallback(callback) } awaitClose { telephonyManager.unregisterTelephonyCallback(callback) }
} }
.onEach { telephonyCallbackEvent.tryEmit(Unit) } .shareIn(scope, SharingStarted.WhileSubscribed())
private fun updateConnectionState(
prevState: MobileConnectionModel,
callbackEvent: CallbackEvent,
): MobileConnectionModel =
when (callbackEvent) {
is CallbackEvent.OnServiceStateChanged -> {
val serviceState = callbackEvent.serviceState
prevState.copy(
isEmergencyOnly = serviceState.isEmergencyOnly,
isRoaming = serviceState.roaming,
operatorAlphaShort = serviceState.operatorAlphaShort,
isInService = Utils.isInService(serviceState),
)
}
is CallbackEvent.OnSignalStrengthChanged -> {
val signalStrength = callbackEvent.signalStrength
val cdmaLevel =
signalStrength.getCellSignalStrengths(CellSignalStrengthCdma::class.java).let {
strengths ->
if (!strengths.isEmpty()) {
strengths[0].level
} else {
CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN
}
}
val primaryLevel = signalStrength.level
prevState.copy(
cdmaLevel = cdmaLevel,
primaryLevel = primaryLevel,
isGsm = signalStrength.isGsm,
)
}
is CallbackEvent.OnDataConnectionStateChanged -> {
prevState.copy(dataConnectionState = callbackEvent.dataState.toDataConnectionType())
}
is CallbackEvent.OnDataActivity -> {
prevState.copy(
dataActivityDirection = callbackEvent.direction.toMobileDataActivityModel()
)
}
is CallbackEvent.OnCarrierNetworkChange -> {
prevState.copy(carrierNetworkChangeActive = callbackEvent.active)
}
is CallbackEvent.OnDisplayInfoChanged -> {
val telephonyDisplayInfo = callbackEvent.telephonyDisplayInfo
val networkType =
if (telephonyDisplayInfo.networkType == NETWORK_TYPE_UNKNOWN) {
UnknownNetworkType
} else if (
telephonyDisplayInfo.overrideNetworkType == OVERRIDE_NETWORK_TYPE_NONE
) {
DefaultNetworkType(
mobileMappingsProxy.toIconKey(telephonyDisplayInfo.networkType)
)
} else {
OverrideNetworkType(
mobileMappingsProxy.toIconKeyOverride(
telephonyDisplayInfo.overrideNetworkType
)
)
}
prevState.copy(resolvedNetworkType = networkType)
}
is CallbackEvent.OnDataEnabledChanged -> {
// Not part of this object, handled in a separate flow
prevState
}
}
override val connectionInfo = run {
val initial = MobileConnectionModel()
callbackEvents
.scan(initial, ::updateConnectionState)
.logDiffsForTable( .logDiffsForTable(
mobileLogger, mobileLogger,
columnPrefix = "MobileConnection ($subId)", columnPrefix = "MobileConnection ($subId)",
initialValue = state, initialValue = initial,
) )
.stateIn(scope, SharingStarted.WhileSubscribed(), state) .stateIn(scope, SharingStarted.WhileSubscribed(), initial)
} }
// This will become variable based on [CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL] override val numberOfLevels =
// once it's wired up inside of [CarrierConfigTracker]. systemUiCarrierConfig.shouldInflateSignalStrength
override val numberOfLevels: StateFlow<Int> = .map { shouldInflate ->
flowOf(DEFAULT_NUM_LEVELS) if (shouldInflate) {
.stateIn(scope, SharingStarted.WhileSubscribed(), DEFAULT_NUM_LEVELS) DEFAULT_NUM_LEVELS + 1
} else {
/** Produces whenever the mobile data setting changes for this subId */ DEFAULT_NUM_LEVELS
private val localMobileDataSettingChangedEvent: Flow<Unit> = conflatedCallbackFlow {
val observer =
object : ContentObserver(null) {
override fun onChange(selfChange: Boolean) {
trySend(Unit)
} }
} }
.stateIn(scope, SharingStarted.WhileSubscribed(), DEFAULT_NUM_LEVELS)
globalSettings.registerContentObserver(
globalSettings.getUriFor("${Global.MOBILE_DATA}$subId"),
/* notifyForDescendants */ true,
observer
)
awaitClose { context.contentResolver.unregisterContentObserver(observer) }
}
/** /**
* There are a few cases where we will need to poll [TelephonyManager] so we can update some * There are a few cases where we will need to poll [TelephonyManager] so we can update some
* internal state where callbacks aren't provided. Any of those events should be merged into * internal state where callbacks aren't provided. Any of those events should be merged into
* this flow, which can be used to trigger the polling. * this flow, which can be used to trigger the polling.
*/ */
private val telephonyPollingEvent: Flow<Unit> = private val telephonyPollingEvent: Flow<Unit> = callbackEvents.map { Unit }
merge(
telephonyCallbackEvent,
localMobileDataSettingChangedEvent,
globalMobileDataSettingChangedEvent,
)
override val cdmaRoaming: StateFlow<Boolean> = override val cdmaRoaming: StateFlow<Boolean> =
telephonyPollingEvent telephonyPollingEvent
@@ -276,22 +293,19 @@ class MobileConnectionRepositoryImpl(
) )
.stateIn(scope, SharingStarted.WhileSubscribed(), defaultNetworkName) .stateIn(scope, SharingStarted.WhileSubscribed(), defaultNetworkName)
override val dataEnabled: StateFlow<Boolean> = run { override val dataEnabled = run {
val initial = dataConnectionAllowed() val initial = telephonyManager.isDataConnectionAllowed
telephonyPollingEvent callbackEvents
.mapLatest { dataConnectionAllowed() } .mapNotNull { (it as? CallbackEvent.OnDataEnabledChanged)?.enabled }
.distinctUntilChanged()
.logDiffsForTable( .logDiffsForTable(
mobileLogger, mobileLogger,
columnPrefix = "", columnPrefix = "",
columnName = "dataEnabled", columnName = "dataEnabled",
initialValue = initial, initialValue = initial
) )
.stateIn(scope, SharingStarted.WhileSubscribed(), initial) .stateIn(scope, SharingStarted.WhileSubscribed(), initial)
} }
private fun dataConnectionAllowed(): Boolean = telephonyManager.isDataConnectionAllowed
class Factory class Factory
@Inject @Inject
constructor( constructor(
@@ -299,7 +313,7 @@ class MobileConnectionRepositoryImpl(
private val context: Context, private val context: Context,
private val telephonyManager: TelephonyManager, private val telephonyManager: TelephonyManager,
private val logger: ConnectivityPipelineLogger, private val logger: ConnectivityPipelineLogger,
private val globalSettings: GlobalSettings, private val carrierConfigRepository: CarrierConfigRepository,
private val mobileMappingsProxy: MobileMappingsProxy, private val mobileMappingsProxy: MobileMappingsProxy,
@Background private val bgDispatcher: CoroutineDispatcher, @Background private val bgDispatcher: CoroutineDispatcher,
@Application private val scope: CoroutineScope, @Application private val scope: CoroutineScope,
@@ -309,7 +323,6 @@ class MobileConnectionRepositoryImpl(
mobileLogger: TableLogBuffer, mobileLogger: TableLogBuffer,
defaultNetworkName: NetworkNameModel, defaultNetworkName: NetworkNameModel,
networkNameSeparator: String, networkNameSeparator: String,
globalMobileDataSettingChangedEvent: Flow<Unit>,
): MobileConnectionRepository { ): MobileConnectionRepository {
return MobileConnectionRepositoryImpl( return MobileConnectionRepositoryImpl(
context, context,
@@ -317,9 +330,8 @@ class MobileConnectionRepositoryImpl(
defaultNetworkName, defaultNetworkName,
networkNameSeparator, networkNameSeparator,
telephonyManager.createForSubscriptionId(subId), telephonyManager.createForSubscriptionId(subId),
globalSettings, carrierConfigRepository.getOrCreateConfigForSubId(subId),
broadcastDispatcher, broadcastDispatcher,
globalMobileDataSettingChangedEvent,
mobileMappingsProxy, mobileMappingsProxy,
bgDispatcher, bgDispatcher,
logger, logger,
@@ -329,3 +341,17 @@ class MobileConnectionRepositoryImpl(
} }
} }
} }
/**
* Wrap every [TelephonyCallback] we care about in a data class so we can accept them in a single
* shared flow and then split them back out into other flows.
*/
private sealed interface CallbackEvent {
data class OnServiceStateChanged(val serviceState: ServiceState) : CallbackEvent
data class OnSignalStrengthChanged(val signalStrength: SignalStrength) : CallbackEvent
data class OnDataConnectionStateChanged(val dataState: Int) : CallbackEvent
data class OnDataActivity(val direction: Int) : CallbackEvent
data class OnCarrierNetworkChange(val active: Boolean) : CallbackEvent
data class OnDisplayInfoChanged(val telephonyDisplayInfo: TelephonyDisplayInfo) : CallbackEvent
data class OnDataEnabledChanged(val enabled: Boolean) : CallbackEvent
}

View File

@@ -19,14 +19,12 @@ package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.content.IntentFilter import android.content.IntentFilter
import android.database.ContentObserver
import android.net.ConnectivityManager import android.net.ConnectivityManager
import android.net.ConnectivityManager.NetworkCallback import android.net.ConnectivityManager.NetworkCallback
import android.net.Network import android.net.Network
import android.net.NetworkCapabilities import android.net.NetworkCapabilities
import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
import android.net.NetworkCapabilities.TRANSPORT_CELLULAR import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
import android.provider.Settings.Global.MOBILE_DATA
import android.telephony.CarrierConfigManager import android.telephony.CarrierConfigManager
import android.telephony.SubscriptionInfo import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager import android.telephony.SubscriptionManager
@@ -54,7 +52,6 @@ import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
import com.android.systemui.util.kotlin.pairwise import com.android.systemui.util.kotlin.pairwise
import com.android.systemui.util.settings.GlobalSettings
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@@ -87,7 +84,6 @@ constructor(
private val logger: ConnectivityPipelineLogger, private val logger: ConnectivityPipelineLogger,
mobileMappingsProxy: MobileMappingsProxy, mobileMappingsProxy: MobileMappingsProxy,
broadcastDispatcher: BroadcastDispatcher, broadcastDispatcher: BroadcastDispatcher,
private val globalSettings: GlobalSettings,
private val context: Context, private val context: Context,
@Background private val bgDispatcher: CoroutineDispatcher, @Background private val bgDispatcher: CoroutineDispatcher,
@Application private val scope: CoroutineScope, @Application private val scope: CoroutineScope,
@@ -222,29 +218,6 @@ constructor(
?: createRepositoryForSubId(subId).also { subIdRepositoryCache[subId] = it } ?: createRepositoryForSubId(subId).also { subIdRepositoryCache[subId] = it }
} }
/**
* In single-SIM devices, the [MOBILE_DATA] setting is phone-wide. For multi-SIM, the individual
* connection repositories also observe the URI for [MOBILE_DATA] + subId.
*/
override val globalMobileDataSettingChangedEvent: Flow<Unit> =
conflatedCallbackFlow {
val observer =
object : ContentObserver(null) {
override fun onChange(selfChange: Boolean) {
trySend(Unit)
}
}
globalSettings.registerContentObserver(
globalSettings.getUriFor(MOBILE_DATA),
true,
observer
)
awaitClose { context.contentResolver.unregisterContentObserver(observer) }
}
.logInputChange(logger, "globalMobileDataSettingChangedEvent")
@SuppressLint("MissingPermission") @SuppressLint("MissingPermission")
override val defaultMobileNetworkConnectivity: StateFlow<MobileConnectivityModel> = override val defaultMobileNetworkConnectivity: StateFlow<MobileConnectivityModel> =
conflatedCallbackFlow { conflatedCallbackFlow {
@@ -315,7 +288,6 @@ constructor(
isCarrierMerged(subId), isCarrierMerged(subId),
defaultNetworkName, defaultNetworkName,
networkNameSeparator, networkNameSeparator,
globalMobileDataSettingChangedEvent,
) )
} }

View File

@@ -231,6 +231,27 @@ constructor(
) )
} }
fun logCarrierConfigChanged(subId: Int) {
buffer.log(
SB_LOGGING_TAG,
LogLevel.INFO,
{ int1 = subId },
{ "onCarrierConfigChanged: subId=$int1" },
)
}
fun logOnDataEnabledChanged(enabled: Boolean, subId: Int) {
buffer.log(
SB_LOGGING_TAG,
LogLevel.INFO,
{
int1 = subId
bool1 = enabled
},
{ "onDataEnabledChanged: subId=$int1 enabled=$bool1" },
)
}
companion object { companion object {
const val SB_LOGGING_TAG = "SbConnectivity" const val SB_LOGGING_TAG = "SbConnectivity"

View File

@@ -0,0 +1,120 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.statusbar.pipeline.mobile.data.model
import android.os.PersistableBundle
import android.telephony.CarrierConfigManager
import android.telephony.CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL
import android.telephony.CarrierConfigManager.KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.junit.Before
import org.junit.Test
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
class SystemUiCarrierConfigTest : SysuiTestCase() {
lateinit var underTest: SystemUiCarrierConfig
@Before
fun setUp() {
underTest = SystemUiCarrierConfig(SUB_1_ID, createTestConfig())
}
@Test
fun `process new config - reflected by isUsingDefault`() {
// Starts out using the defaults
assertThat(underTest.isUsingDefault).isTrue()
// ANY new config means we're no longer tracking defaults
underTest.processNewCarrierConfig(createTestConfig())
assertThat(underTest.isUsingDefault).isFalse()
}
@Test
fun `process new config - updates all flows`() {
assertThat(underTest.shouldInflateSignalStrength.value).isFalse()
assertThat(underTest.showOperatorNameInStatusBar.value).isFalse()
underTest.processNewCarrierConfig(
configWithOverrides(
KEY_INFLATE_SIGNAL_STRENGTH_BOOL to true,
KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL to true,
)
)
assertThat(underTest.shouldInflateSignalStrength.value).isTrue()
assertThat(underTest.showOperatorNameInStatusBar.value).isTrue()
}
@Test
fun `process new config - defaults to false for config overrides`() {
// This case is only apparent when:
// 1. The default is true
// 2. The override config has no value for a given key
// In this case (per the old code) we would use the default value of false, despite there
// being no override key present in the override config
underTest =
SystemUiCarrierConfig(
SUB_1_ID,
configWithOverrides(
KEY_INFLATE_SIGNAL_STRENGTH_BOOL to true,
KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL to true,
)
)
assertThat(underTest.isUsingDefault).isTrue()
assertThat(underTest.shouldInflateSignalStrength.value).isTrue()
assertThat(underTest.showOperatorNameInStatusBar.value).isTrue()
// Process a new config with no keys
underTest.processNewCarrierConfig(PersistableBundle())
assertThat(underTest.isUsingDefault).isFalse()
assertThat(underTest.shouldInflateSignalStrength.value).isFalse()
assertThat(underTest.showOperatorNameInStatusBar.value).isFalse()
}
companion object {
private const val SUB_1_ID = 1
/**
* In order to keep us from having to update every place that might want to create a config,
* make sure to add new keys here
*/
fun createTestConfig() =
PersistableBundle().also {
it.putBoolean(CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL, false)
it.putBoolean(CarrierConfigManager.KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL, false)
}
/** Override the default config with the given (key, value) pair */
fun configWithOverride(key: String, override: Boolean): PersistableBundle =
createTestConfig().also { it.putBoolean(key, override) }
/** Override any number of configs from the default */
fun configWithOverrides(vararg overrides: Pair<String, Boolean>) =
createTestConfig().also { config ->
overrides.forEach { (key, value) -> config.putBoolean(key, value) }
}
}
}

View File

@@ -0,0 +1,172 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.statusbar.pipeline.mobile.data.repository
import android.content.Intent
import android.os.PersistableBundle
import android.telephony.CarrierConfigManager
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import androidx.test.filters.SmallTest
import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfigTest.Companion.createTestConfig
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.MockitoAnnotations
import org.mockito.MockitoSession
import org.mockito.quality.Strictness
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
class CarrierConfigRepositoryTest : SysuiTestCase() {
private val testDispatcher = UnconfinedTestDispatcher()
private val testScope = TestScope(testDispatcher)
private lateinit var underTest: CarrierConfigRepository
private lateinit var mockitoSession: MockitoSession
private lateinit var carrierConfigCoreStartable: CarrierConfigCoreStartable
@Mock private lateinit var logger: ConnectivityPipelineLogger
@Mock private lateinit var carrierConfigManager: CarrierConfigManager
@Mock private lateinit var dumpManager: DumpManager
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
mockitoSession =
mockitoSession()
.initMocks(this)
.mockStatic(CarrierConfigManager::class.java)
.strictness(Strictness.LENIENT)
.startMocking()
whenever(CarrierConfigManager.getDefaultConfig()).thenReturn(DEFAULT_CONFIG)
whenever(carrierConfigManager.getConfigForSubId(anyInt())).thenAnswer { invocation ->
when (invocation.getArgument(0) as Int) {
1 -> CONFIG_1
2 -> CONFIG_2
else -> null
}
}
underTest =
CarrierConfigRepository(
fakeBroadcastDispatcher,
carrierConfigManager,
dumpManager,
logger,
testScope.backgroundScope,
)
carrierConfigCoreStartable =
CarrierConfigCoreStartable(underTest, testScope.backgroundScope)
}
@After
fun tearDown() {
mockitoSession.finishMocking()
}
@Test
fun `carrier config stream produces int-bundle pairs`() =
testScope.runTest {
var latest: Pair<Int, PersistableBundle>? = null
val job = underTest.carrierConfigStream.onEach { latest = it }.launchIn(this)
sendConfig(SUB_ID_1)
assertThat(latest).isEqualTo(Pair(SUB_ID_1, CONFIG_1))
sendConfig(SUB_ID_2)
assertThat(latest).isEqualTo(Pair(SUB_ID_2, CONFIG_2))
job.cancel()
}
@Test
fun `carrier config stream ignores invalid subscriptions`() =
testScope.runTest {
var latest: Pair<Int, PersistableBundle>? = null
val job = underTest.carrierConfigStream.onEach { latest = it }.launchIn(this)
sendConfig(INVALID_SUBSCRIPTION_ID)
assertThat(latest).isNull()
job.cancel()
}
@Test
fun `getOrCreateConfig - uses default config if no override`() {
val config = underTest.getOrCreateConfigForSubId(123)
assertThat(config.isUsingDefault).isTrue()
}
@Test
fun `getOrCreateConfig - uses override if exists`() {
val config = underTest.getOrCreateConfigForSubId(SUB_ID_1)
assertThat(config.isUsingDefault).isFalse()
}
@Test
fun `config - updates while config stream is collected`() =
testScope.runTest {
CONFIG_1.putBoolean(CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL, false)
carrierConfigCoreStartable.start()
val config = underTest.getOrCreateConfigForSubId(SUB_ID_1)
assertThat(config.shouldInflateSignalStrength.value).isFalse()
CONFIG_1.putBoolean(CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL, true)
sendConfig(SUB_ID_1)
assertThat(config.shouldInflateSignalStrength.value).isTrue()
}
private fun sendConfig(subId: Int) {
fakeBroadcastDispatcher.registeredReceivers.forEach { receiver ->
receiver.onReceive(
context,
Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)
.putExtra(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX, subId)
)
}
}
companion object {
private const val SUB_ID_1 = 1
private const val SUB_ID_2 = 2
private val DEFAULT_CONFIG = createTestConfig()
private val CONFIG_1 = createTestConfig()
private val CONFIG_2 = createTestConfig()
}
}

View File

@@ -71,9 +71,6 @@ class FakeMobileConnectionsRepository(
?: FakeMobileConnectionRepository(subId, tableLogBuffer).also { subIdRepos[subId] = it } ?: FakeMobileConnectionRepository(subId, tableLogBuffer).also { subIdRepos[subId] = it }
} }
private val _globalMobileDataSettingChangedEvent = MutableStateFlow(Unit)
override val globalMobileDataSettingChangedEvent = _globalMobileDataSettingChangedEvent
override val defaultDataSubRatConfig = MutableStateFlow(MobileMappings.Config()) override val defaultDataSubRatConfig = MutableStateFlow(MobileMappings.Config())
private val _defaultMobileIconMapping = MutableStateFlow(TEST_MAPPING) private val _defaultMobileIconMapping = MutableStateFlow(TEST_MAPPING)
@@ -94,10 +91,6 @@ class FakeMobileConnectionsRepository(
_mobileConnectivity.value = model _mobileConnectivity.value = model
} }
suspend fun triggerGlobalMobileDataSettingChangedEvent() {
_globalMobileDataSettingChangedEvent.emit(Unit)
}
fun setActiveMobileDataSubscriptionId(subId: Int) { fun setActiveMobileDataSubscriptionId(subId: Int) {
_activeMobileDataSubscriptionId.value = subId _activeMobileDataSubscriptionId.value = subId
} }

View File

@@ -40,7 +40,6 @@ import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.kotlinArgumentCaptor import com.android.systemui.util.mockito.kotlinArgumentCaptor
import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.util.time.FakeSystemClock import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@@ -85,7 +84,6 @@ class MobileRepositorySwitcherTest : SysuiTestCase() {
@Mock private lateinit var demoModeController: DemoModeController @Mock private lateinit var demoModeController: DemoModeController
@Mock private lateinit var dumpManager: DumpManager @Mock private lateinit var dumpManager: DumpManager
private val globalSettings = FakeSettings()
private val fakeNetworkEventsFlow = MutableStateFlow<FakeNetworkEventModel?>(null) private val fakeNetworkEventsFlow = MutableStateFlow<FakeNetworkEventModel?>(null)
private val mobileMappings = FakeMobileMappingsProxy() private val mobileMappings = FakeMobileMappingsProxy()
@@ -118,7 +116,6 @@ class MobileRepositorySwitcherTest : SysuiTestCase() {
logger, logger,
mobileMappings, mobileMappings,
fakeBroadcastDispatcher, fakeBroadcastDispatcher,
globalSettings,
context, context,
IMMEDIATE, IMMEDIATE,
scope, scope,

View File

@@ -23,9 +23,7 @@ import com.android.systemui.log.table.TableLogBufferFactory
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.mock
@@ -33,7 +31,6 @@ import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.TestScope
@@ -57,22 +54,15 @@ class FullMobileConnectionRepositoryTest : SysuiTestCase() {
private val testDispatcher = UnconfinedTestDispatcher() private val testDispatcher = UnconfinedTestDispatcher()
private val testScope = TestScope(testDispatcher) private val testScope = TestScope(testDispatcher)
private val mobileMappings = FakeMobileMappingsProxy()
private val tableLogBuffer = mock<TableLogBuffer>() private val tableLogBuffer = mock<TableLogBuffer>()
private val mobileFactory = mock<MobileConnectionRepositoryImpl.Factory>() private val mobileFactory = mock<MobileConnectionRepositoryImpl.Factory>()
private val carrierMergedFactory = mock<CarrierMergedConnectionRepository.Factory>() private val carrierMergedFactory = mock<CarrierMergedConnectionRepository.Factory>()
private lateinit var connectionsRepo: FakeMobileConnectionsRepository
private val globalMobileDataSettingChangedEvent: Flow<Unit>
get() = connectionsRepo.globalMobileDataSettingChangedEvent
private lateinit var mobileRepo: FakeMobileConnectionRepository private lateinit var mobileRepo: FakeMobileConnectionRepository
private lateinit var carrierMergedRepo: FakeMobileConnectionRepository private lateinit var carrierMergedRepo: FakeMobileConnectionRepository
@Before @Before
fun setUp() { fun setUp() {
connectionsRepo = FakeMobileConnectionsRepository(mobileMappings, tableLogBuffer)
mobileRepo = FakeMobileConnectionRepository(SUB_ID, tableLogBuffer) mobileRepo = FakeMobileConnectionRepository(SUB_ID, tableLogBuffer)
carrierMergedRepo = FakeMobileConnectionRepository(SUB_ID, tableLogBuffer) carrierMergedRepo = FakeMobileConnectionRepository(SUB_ID, tableLogBuffer)
@@ -82,7 +72,6 @@ class FullMobileConnectionRepositoryTest : SysuiTestCase() {
any(), any(),
eq(DEFAULT_NAME), eq(DEFAULT_NAME),
eq(SEP), eq(SEP),
eq(globalMobileDataSettingChangedEvent),
) )
) )
.thenReturn(mobileRepo) .thenReturn(mobileRepo)
@@ -109,7 +98,6 @@ class FullMobileConnectionRepositoryTest : SysuiTestCase() {
tableLogBuffer, tableLogBuffer,
DEFAULT_NAME, DEFAULT_NAME,
SEP, SEP,
globalMobileDataSettingChangedEvent
) )
} }
@@ -310,7 +298,6 @@ class FullMobileConnectionRepositoryTest : SysuiTestCase() {
startingIsCarrierMerged = false, startingIsCarrierMerged = false,
DEFAULT_NAME, DEFAULT_NAME,
SEP, SEP,
globalMobileDataSettingChangedEvent,
) )
val connection1Repeat = val connection1Repeat =
@@ -319,7 +306,6 @@ class FullMobileConnectionRepositoryTest : SysuiTestCase() {
startingIsCarrierMerged = false, startingIsCarrierMerged = false,
DEFAULT_NAME, DEFAULT_NAME,
SEP, SEP,
globalMobileDataSettingChangedEvent,
) )
assertThat(connection1.tableLogBuffer) assertThat(connection1.tableLogBuffer)
@@ -345,7 +331,6 @@ class FullMobileConnectionRepositoryTest : SysuiTestCase() {
startingIsCarrierMerged = false, startingIsCarrierMerged = false,
DEFAULT_NAME, DEFAULT_NAME,
SEP, SEP,
globalMobileDataSettingChangedEvent,
) )
// WHEN a connection with the same sub ID but carrierMerged = true is created // WHEN a connection with the same sub ID but carrierMerged = true is created
@@ -355,7 +340,6 @@ class FullMobileConnectionRepositoryTest : SysuiTestCase() {
startingIsCarrierMerged = true, startingIsCarrierMerged = true,
DEFAULT_NAME, DEFAULT_NAME,
SEP, SEP,
globalMobileDataSettingChangedEvent,
) )
// THEN the same table is re-used // THEN the same table is re-used
@@ -374,7 +358,6 @@ class FullMobileConnectionRepositoryTest : SysuiTestCase() {
tableLogBuffer, tableLogBuffer,
DEFAULT_NAME, DEFAULT_NAME,
SEP, SEP,
globalMobileDataSettingChangedEvent,
testScope.backgroundScope, testScope.backgroundScope,
mobileFactory, mobileFactory,
carrierMergedFactory, carrierMergedFactory,

View File

@@ -17,8 +17,7 @@
package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod
import android.content.Intent import android.content.Intent
import android.os.UserHandle import android.telephony.CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL
import android.provider.Settings
import android.telephony.CellSignalStrengthCdma import android.telephony.CellSignalStrengthCdma
import android.telephony.NetworkRegistrationInfo import android.telephony.NetworkRegistrationInfo
import android.telephony.ServiceState import android.telephony.ServiceState
@@ -61,6 +60,9 @@ import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameMode
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.DefaultNetworkType import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.DefaultNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.OverrideNetworkType import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.OverrideNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.UnknownNetworkType import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.UnknownNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig
import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfigTest.Companion.configWithOverride
import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfigTest.Companion.createTestConfig
import com.android.systemui.statusbar.pipeline.mobile.data.model.toNetworkNameModel import com.android.systemui.statusbar.pipeline.mobile.data.model.toNetworkNameModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
@@ -72,7 +74,6 @@ import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.settings.FakeSettings
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -101,12 +102,15 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
private val scope = CoroutineScope(IMMEDIATE) private val scope = CoroutineScope(IMMEDIATE)
private val mobileMappings = FakeMobileMappingsProxy() private val mobileMappings = FakeMobileMappingsProxy()
private val globalSettings = FakeSettings() private val systemUiCarrierConfig =
SystemUiCarrierConfig(
SUB_1_ID,
createTestConfig(),
)
@Before @Before
fun setUp() { fun setUp() {
MockitoAnnotations.initMocks(this) MockitoAnnotations.initMocks(this)
globalSettings.userId = UserHandle.USER_ALL
whenever(telephonyManager.subscriptionId).thenReturn(SUB_1_ID) whenever(telephonyManager.subscriptionId).thenReturn(SUB_1_ID)
connectionsRepo = FakeMobileConnectionsRepository(mobileMappings, tableLogger) connectionsRepo = FakeMobileConnectionsRepository(mobileMappings, tableLogger)
@@ -118,9 +122,8 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
DEFAULT_NAME, DEFAULT_NAME,
SEP, SEP,
telephonyManager, telephonyManager,
globalSettings, systemUiCarrierConfig,
fakeBroadcastDispatcher, fakeBroadcastDispatcher,
connectionsRepo.globalMobileDataSettingChangedEvent,
mobileMappings, mobileMappings,
IMMEDIATE, IMMEDIATE,
logger, logger,
@@ -399,53 +402,27 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
@Test @Test
fun dataEnabled_initial_false() = fun dataEnabled_initial_false() =
runBlocking(IMMEDIATE) {
whenever(telephonyManager.isDataConnectionAllowed).thenReturn(true)
assertThat(underTest.dataEnabled.value).isFalse()
}
@Test
fun dataEnabled_isEnabled_true() =
runBlocking(IMMEDIATE) {
whenever(telephonyManager.isDataConnectionAllowed).thenReturn(true)
val job = underTest.dataEnabled.launchIn(this)
assertThat(underTest.dataEnabled.value).isTrue()
job.cancel()
}
@Test
fun dataEnabled_isDisabled() =
runBlocking(IMMEDIATE) { runBlocking(IMMEDIATE) {
whenever(telephonyManager.isDataConnectionAllowed).thenReturn(false) whenever(telephonyManager.isDataConnectionAllowed).thenReturn(false)
val job = underTest.dataEnabled.launchIn(this)
assertThat(underTest.dataEnabled.value).isFalse() assertThat(underTest.dataEnabled.value).isFalse()
job.cancel()
} }
@Test @Test
fun isDataConnectionAllowed_subIdSettingUpdate_valueUpdated() = fun `is data enabled - tracks telephony callback`() =
runBlocking(IMMEDIATE) { runBlocking(IMMEDIATE) {
val subIdSettingName = "${Settings.Global.MOBILE_DATA}$SUB_1_ID"
var latest: Boolean? = null var latest: Boolean? = null
val job = underTest.dataEnabled.onEach { latest = it }.launchIn(this) val job = underTest.dataEnabled.onEach { latest = it }.launchIn(this)
// We don't read the setting directly, we query telephony when changes happen
whenever(telephonyManager.isDataConnectionAllowed).thenReturn(false) whenever(telephonyManager.isDataConnectionAllowed).thenReturn(false)
globalSettings.putInt(subIdSettingName, 0) assertThat(underTest.dataEnabled.value).isFalse()
assertThat(latest).isFalse()
whenever(telephonyManager.isDataConnectionAllowed).thenReturn(true) val callback = getTelephonyCallbackForType<TelephonyCallback.DataEnabledListener>()
globalSettings.putInt(subIdSettingName, 1)
callback.onDataEnabledChanged(true, 1)
assertThat(latest).isTrue() assertThat(latest).isTrue()
whenever(telephonyManager.isDataConnectionAllowed).thenReturn(false) callback.onDataEnabledChanged(false, 1)
globalSettings.putInt(subIdSettingName, 0)
assertThat(latest).isFalse() assertThat(latest).isFalse()
job.cancel() job.cancel()
@@ -466,8 +443,6 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
fun `roaming - cdma - queries telephony manager`() = fun `roaming - cdma - queries telephony manager`() =
runBlocking(IMMEDIATE) { runBlocking(IMMEDIATE) {
var latest: Boolean? = null var latest: Boolean? = null
// Start the telephony collection job so that cdmaRoaming starts updating
val telephonyJob = underTest.connectionInfo.launchIn(this)
val job = underTest.cdmaRoaming.onEach { latest = it }.launchIn(this) val job = underTest.cdmaRoaming.onEach { latest = it }.launchIn(this)
val cb = getTelephonyCallbackForType<ServiceStateListener>() val cb = getTelephonyCallbackForType<ServiceStateListener>()
@@ -487,7 +462,6 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
assertThat(latest).isTrue() assertThat(latest).isTrue()
telephonyJob.cancel()
job.cancel() job.cancel()
} }
@@ -673,6 +647,29 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
job.cancel() job.cancel()
} }
@Test
fun `number of levels - uses carrier config`() =
runBlocking(IMMEDIATE) {
var latest: Int? = null
val job = underTest.numberOfLevels.onEach { latest = it }.launchIn(this)
assertThat(latest).isEqualTo(DEFAULT_NUM_LEVELS)
systemUiCarrierConfig.processNewCarrierConfig(
configWithOverride(KEY_INFLATE_SIGNAL_STRENGTH_BOOL, true)
)
assertThat(latest).isEqualTo(DEFAULT_NUM_LEVELS + 1)
systemUiCarrierConfig.processNewCarrierConfig(
configWithOverride(KEY_INFLATE_SIGNAL_STRENGTH_BOOL, false)
)
assertThat(latest).isEqualTo(DEFAULT_NUM_LEVELS)
job.cancel()
}
private fun getTelephonyCallbacks(): List<TelephonyCallback> { private fun getTelephonyCallbacks(): List<TelephonyCallback> {
val callbackCaptor = argumentCaptor<TelephonyCallback>() val callbackCaptor = argumentCaptor<TelephonyCallback>()
Mockito.verify(telephonyManager).registerTelephonyCallback(any(), callbackCaptor.capture()) Mockito.verify(telephonyManager).registerTelephonyCallback(any(), callbackCaptor.capture())

View File

@@ -23,7 +23,6 @@ import android.net.NetworkCapabilities
import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
import android.net.NetworkCapabilities.TRANSPORT_CELLULAR import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
import android.os.ParcelUuid import android.os.ParcelUuid
import android.provider.Settings
import android.telephony.CarrierConfigManager import android.telephony.CarrierConfigManager
import android.telephony.SubscriptionInfo import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager import android.telephony.SubscriptionManager
@@ -39,6 +38,7 @@ import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.TableLogBufferFactory import com.android.systemui.log.table.TableLogBufferFactory
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.CarrierConfigRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Factory.Companion.tableBufferLogName import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Factory.Companion.tableBufferLogName
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
@@ -49,7 +49,6 @@ import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.settings.FakeSettings
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import java.util.UUID import java.util.UUID
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@@ -80,6 +79,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
private lateinit var carrierMergedFactory: CarrierMergedConnectionRepository.Factory private lateinit var carrierMergedFactory: CarrierMergedConnectionRepository.Factory
private lateinit var fullConnectionFactory: FullMobileConnectionRepository.Factory private lateinit var fullConnectionFactory: FullMobileConnectionRepository.Factory
private lateinit var wifiRepository: FakeWifiRepository private lateinit var wifiRepository: FakeWifiRepository
private lateinit var carrierConfigRepository: CarrierConfigRepository
@Mock private lateinit var connectivityManager: ConnectivityManager @Mock private lateinit var connectivityManager: ConnectivityManager
@Mock private lateinit var subscriptionManager: SubscriptionManager @Mock private lateinit var subscriptionManager: SubscriptionManager
@Mock private lateinit var telephonyManager: TelephonyManager @Mock private lateinit var telephonyManager: TelephonyManager
@@ -89,7 +89,6 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
private val mobileMappings = FakeMobileMappingsProxy() private val mobileMappings = FakeMobileMappingsProxy()
private val scope = CoroutineScope(IMMEDIATE) private val scope = CoroutineScope(IMMEDIATE)
private val globalSettings = FakeSettings()
@Before @Before
fun setUp() { fun setUp() {
@@ -119,16 +118,25 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
wifiRepository = FakeWifiRepository() wifiRepository = FakeWifiRepository()
carrierConfigRepository =
CarrierConfigRepository(
fakeBroadcastDispatcher,
mock(),
mock(),
logger,
scope,
)
connectionFactory = connectionFactory =
MobileConnectionRepositoryImpl.Factory( MobileConnectionRepositoryImpl.Factory(
fakeBroadcastDispatcher, fakeBroadcastDispatcher,
context = context, context = context,
telephonyManager = telephonyManager, telephonyManager = telephonyManager,
bgDispatcher = IMMEDIATE, bgDispatcher = IMMEDIATE,
globalSettings = globalSettings,
logger = logger, logger = logger,
mobileMappingsProxy = mobileMappings, mobileMappingsProxy = mobileMappings,
scope = scope, scope = scope,
carrierConfigRepository = carrierConfigRepository,
) )
carrierMergedFactory = carrierMergedFactory =
CarrierMergedConnectionRepository.Factory( CarrierMergedConnectionRepository.Factory(
@@ -151,7 +159,6 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
logger, logger,
mobileMappings, mobileMappings,
fakeBroadcastDispatcher, fakeBroadcastDispatcher,
globalSettings,
context, context,
IMMEDIATE, IMMEDIATE,
scope, scope,
@@ -543,24 +550,6 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
job.cancel() job.cancel()
} }
@Test
fun globalMobileDataSettingsChangedEvent_producesOnSettingChange() =
runBlocking(IMMEDIATE) {
var produced = false
val job =
underTest.globalMobileDataSettingChangedEvent
.onEach { produced = true }
.launchIn(this)
assertThat(produced).isFalse()
globalSettings.putInt(Settings.Global.MOBILE_DATA, 0)
assertThat(produced).isTrue()
job.cancel()
}
@Test @Test
fun mobileConnectivity_isConnected_isNotValidated() = fun mobileConnectivity_isConnected_isNotValidated() =
runBlocking(IMMEDIATE) { runBlocking(IMMEDIATE) {
@@ -629,7 +618,6 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
logger, logger,
mobileMappings, mobileMappings,
fakeBroadcastDispatcher, fakeBroadcastDispatcher,
globalSettings,
context, context,
IMMEDIATE, IMMEDIATE,
scope, scope,