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:
@@ -49,6 +49,7 @@
|
||||
<permission name="android.permission.READ_FRAME_BUFFER"/>
|
||||
<permission name="android.permission.READ_NETWORK_USAGE_HISTORY"/>
|
||||
<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.REQUEST_NETWORK_SCORES"/>
|
||||
<permission name="android.permission.RECEIVE_MEDIA_RESOURCE_USAGE"/>
|
||||
|
||||
@@ -68,6 +68,7 @@
|
||||
<uses-permission android:name="android.permission.ACCESS_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_PRECISE_PHONE_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.OVERRIDE_WIFI_CONFIG" />
|
||||
|
||||
@@ -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.ui.viewmodel.AirplaneModeViewModel
|
||||
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.MobileRepositorySwitcher
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupRepository
|
||||
@@ -82,6 +83,11 @@ abstract class StatusBarPipelineModule {
|
||||
@ClassKey(MobileUiAdapter::class)
|
||||
abstract fun bindFeature(impl: MobileUiAdapter): CoreStartable
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(CarrierConfigCoreStartable::class)
|
||||
abstract fun bindCarrierConfigStartable(impl: CarrierConfigCoreStartable): CoreStartable
|
||||
|
||||
companion object {
|
||||
@Provides
|
||||
@SysUISingleton
|
||||
|
||||
@@ -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}"
|
||||
}
|
||||
}
|
||||
@@ -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() }
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package com.android.systemui.statusbar.pipeline.mobile.data.repository
|
||||
|
||||
import android.provider.Settings
|
||||
import android.telephony.CarrierConfigManager
|
||||
import android.telephony.SubscriptionManager
|
||||
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 */
|
||||
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.
|
||||
* In the case of [MobileMappings], it's hard-coded to check the default data subscription's
|
||||
|
||||
@@ -156,9 +156,6 @@ constructor(
|
||||
realRepository.defaultMobileNetworkConnectivity.value
|
||||
)
|
||||
|
||||
override val globalMobileDataSettingChangedEvent: Flow<Unit> =
|
||||
activeRepo.flatMapLatest { it.globalMobileDataSettingChangedEvent }
|
||||
|
||||
override fun getRepoForSubId(subId: Int): MobileConnectionRepository {
|
||||
if (isDemoMode.value) {
|
||||
return demoMobileConnectionsRepository.getRepoForSubId(subId)
|
||||
|
||||
@@ -185,8 +185,6 @@ constructor(
|
||||
return CacheContainer(repo, lastMobileState = null)
|
||||
}
|
||||
|
||||
override val globalMobileDataSettingChangedEvent = MutableStateFlow(Unit)
|
||||
|
||||
fun startProcessingCommands() {
|
||||
mobileDemoCommandJob =
|
||||
scope.launch {
|
||||
|
||||
@@ -26,7 +26,6 @@ import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConn
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
@@ -50,7 +49,6 @@ class FullMobileConnectionRepository(
|
||||
override val tableLogBuffer: TableLogBuffer,
|
||||
private val defaultNetworkName: NetworkNameModel,
|
||||
private val networkNameSeparator: String,
|
||||
private val globalMobileDataSettingChangedEvent: Flow<Unit>,
|
||||
@Application scope: CoroutineScope,
|
||||
private val mobileRepoFactory: MobileConnectionRepositoryImpl.Factory,
|
||||
private val carrierMergedRepoFactory: CarrierMergedConnectionRepository.Factory,
|
||||
@@ -84,7 +82,6 @@ class FullMobileConnectionRepository(
|
||||
tableLogBuffer,
|
||||
defaultNetworkName,
|
||||
networkNameSeparator,
|
||||
globalMobileDataSettingChangedEvent,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -150,7 +147,6 @@ class FullMobileConnectionRepository(
|
||||
startingIsCarrierMerged: Boolean,
|
||||
defaultNetworkName: NetworkNameModel,
|
||||
networkNameSeparator: String,
|
||||
globalMobileDataSettingChangedEvent: Flow<Unit>,
|
||||
): FullMobileConnectionRepository {
|
||||
val mobileLogger =
|
||||
logFactory.getOrCreate(tableBufferLogName(subId), MOBILE_CONNECTION_BUFFER_SIZE)
|
||||
@@ -161,7 +157,6 @@ class FullMobileConnectionRepository(
|
||||
mobileLogger,
|
||||
defaultNetworkName,
|
||||
networkNameSeparator,
|
||||
globalMobileDataSettingChangedEvent,
|
||||
scope,
|
||||
mobileRepoFactory,
|
||||
carrierMergedRepoFactory,
|
||||
|
||||
@@ -18,8 +18,6 @@ package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod
|
||||
|
||||
import android.content.Context
|
||||
import android.content.IntentFilter
|
||||
import android.database.ContentObserver
|
||||
import android.provider.Settings.Global
|
||||
import android.telephony.CellSignalStrength
|
||||
import android.telephony.CellSignalStrengthCdma
|
||||
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.OverrideNetworkType
|
||||
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.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.Companion.DEFAULT_NUM_LEVELS
|
||||
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
|
||||
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
|
||||
import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
|
||||
import com.android.systemui.util.settings.GlobalSettings
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -60,13 +59,15 @@ import kotlinx.coroutines.asExecutor
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.mapLatest
|
||||
import kotlinx.coroutines.flow.merge
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
import kotlinx.coroutines.flow.scan
|
||||
import kotlinx.coroutines.flow.shareIn
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
||||
/**
|
||||
@@ -81,10 +82,9 @@ class MobileConnectionRepositoryImpl(
|
||||
defaultNetworkName: NetworkNameModel,
|
||||
networkNameSeparator: String,
|
||||
private val telephonyManager: TelephonyManager,
|
||||
private val globalSettings: GlobalSettings,
|
||||
systemUiCarrierConfig: SystemUiCarrierConfig,
|
||||
broadcastDispatcher: BroadcastDispatcher,
|
||||
globalMobileDataSettingChangedEvent: Flow<Unit>,
|
||||
mobileMappingsProxy: MobileMappingsProxy,
|
||||
private val mobileMappingsProxy: MobileMappingsProxy,
|
||||
bgDispatcher: CoroutineDispatcher,
|
||||
logger: ConnectivityPipelineLogger,
|
||||
mobileLogger: TableLogBuffer,
|
||||
@@ -103,8 +103,15 @@ class MobileConnectionRepositoryImpl(
|
||||
|
||||
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 {
|
||||
val callback =
|
||||
object :
|
||||
@@ -114,41 +121,16 @@ class MobileConnectionRepositoryImpl(
|
||||
TelephonyCallback.DataConnectionStateListener,
|
||||
TelephonyCallback.DataActivityListener,
|
||||
TelephonyCallback.CarrierNetworkListener,
|
||||
TelephonyCallback.DisplayInfoListener {
|
||||
TelephonyCallback.DisplayInfoListener,
|
||||
TelephonyCallback.DataEnabledListener {
|
||||
override fun onServiceStateChanged(serviceState: ServiceState) {
|
||||
logger.logOnServiceStateChanged(serviceState, subId)
|
||||
state =
|
||||
state.copy(
|
||||
isEmergencyOnly = serviceState.isEmergencyOnly,
|
||||
isRoaming = serviceState.roaming,
|
||||
operatorAlphaShort = serviceState.operatorAlphaShort,
|
||||
isInService = Utils.isInService(serviceState),
|
||||
)
|
||||
trySend(state)
|
||||
trySend(CallbackEvent.OnServiceStateChanged(serviceState))
|
||||
}
|
||||
|
||||
override fun onSignalStrengthsChanged(signalStrength: SignalStrength) {
|
||||
logger.logOnSignalStrengthsChanged(signalStrength, subId)
|
||||
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
|
||||
|
||||
state =
|
||||
state.copy(
|
||||
cdmaLevel = cdmaLevel,
|
||||
primaryLevel = primaryLevel,
|
||||
isGsm = signalStrength.isGsm,
|
||||
)
|
||||
trySend(state)
|
||||
trySend(CallbackEvent.OnSignalStrengthChanged(signalStrength))
|
||||
}
|
||||
|
||||
override fun onDataConnectionStateChanged(
|
||||
@@ -156,101 +138,136 @@ class MobileConnectionRepositoryImpl(
|
||||
networkType: Int
|
||||
) {
|
||||
logger.logOnDataConnectionStateChanged(dataState, networkType, subId)
|
||||
state =
|
||||
state.copy(dataConnectionState = dataState.toDataConnectionType())
|
||||
trySend(state)
|
||||
trySend(CallbackEvent.OnDataConnectionStateChanged(dataState))
|
||||
}
|
||||
|
||||
override fun onDataActivity(direction: Int) {
|
||||
logger.logOnDataActivity(direction, subId)
|
||||
state =
|
||||
state.copy(
|
||||
dataActivityDirection = direction.toMobileDataActivityModel()
|
||||
)
|
||||
trySend(state)
|
||||
trySend(CallbackEvent.OnDataActivity(direction))
|
||||
}
|
||||
|
||||
override fun onCarrierNetworkChange(active: Boolean) {
|
||||
logger.logOnCarrierNetworkChange(active, subId)
|
||||
state = state.copy(carrierNetworkChangeActive = active)
|
||||
trySend(state)
|
||||
trySend(CallbackEvent.OnCarrierNetworkChange(active))
|
||||
}
|
||||
|
||||
override fun onDisplayInfoChanged(
|
||||
telephonyDisplayInfo: TelephonyDisplayInfo
|
||||
) {
|
||||
logger.logOnDisplayInfoChanged(telephonyDisplayInfo, subId)
|
||||
trySend(CallbackEvent.OnDisplayInfoChanged(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
|
||||
)
|
||||
)
|
||||
}
|
||||
state = state.copy(resolvedNetworkType = networkType)
|
||||
trySend(state)
|
||||
override fun onDataEnabledChanged(enabled: Boolean, reason: Int) {
|
||||
logger.logOnDataEnabledChanged(enabled, subId)
|
||||
trySend(CallbackEvent.OnDataEnabledChanged(enabled))
|
||||
}
|
||||
}
|
||||
telephonyManager.registerTelephonyCallback(bgDispatcher.asExecutor(), 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(
|
||||
mobileLogger,
|
||||
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]
|
||||
// once it's wired up inside of [CarrierConfigTracker].
|
||||
override val numberOfLevels: StateFlow<Int> =
|
||||
flowOf(DEFAULT_NUM_LEVELS)
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), DEFAULT_NUM_LEVELS)
|
||||
|
||||
/** Produces whenever the mobile data setting changes for this subId */
|
||||
private val localMobileDataSettingChangedEvent: Flow<Unit> = conflatedCallbackFlow {
|
||||
val observer =
|
||||
object : ContentObserver(null) {
|
||||
override fun onChange(selfChange: Boolean) {
|
||||
trySend(Unit)
|
||||
override val numberOfLevels =
|
||||
systemUiCarrierConfig.shouldInflateSignalStrength
|
||||
.map { shouldInflate ->
|
||||
if (shouldInflate) {
|
||||
DEFAULT_NUM_LEVELS + 1
|
||||
} else {
|
||||
DEFAULT_NUM_LEVELS
|
||||
}
|
||||
}
|
||||
|
||||
globalSettings.registerContentObserver(
|
||||
globalSettings.getUriFor("${Global.MOBILE_DATA}$subId"),
|
||||
/* notifyForDescendants */ true,
|
||||
observer
|
||||
)
|
||||
|
||||
awaitClose { context.contentResolver.unregisterContentObserver(observer) }
|
||||
}
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), DEFAULT_NUM_LEVELS)
|
||||
|
||||
/**
|
||||
* 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
|
||||
* this flow, which can be used to trigger the polling.
|
||||
*/
|
||||
private val telephonyPollingEvent: Flow<Unit> =
|
||||
merge(
|
||||
telephonyCallbackEvent,
|
||||
localMobileDataSettingChangedEvent,
|
||||
globalMobileDataSettingChangedEvent,
|
||||
)
|
||||
private val telephonyPollingEvent: Flow<Unit> = callbackEvents.map { Unit }
|
||||
|
||||
override val cdmaRoaming: StateFlow<Boolean> =
|
||||
telephonyPollingEvent
|
||||
@@ -276,22 +293,19 @@ class MobileConnectionRepositoryImpl(
|
||||
)
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), defaultNetworkName)
|
||||
|
||||
override val dataEnabled: StateFlow<Boolean> = run {
|
||||
val initial = dataConnectionAllowed()
|
||||
telephonyPollingEvent
|
||||
.mapLatest { dataConnectionAllowed() }
|
||||
.distinctUntilChanged()
|
||||
override val dataEnabled = run {
|
||||
val initial = telephonyManager.isDataConnectionAllowed
|
||||
callbackEvents
|
||||
.mapNotNull { (it as? CallbackEvent.OnDataEnabledChanged)?.enabled }
|
||||
.logDiffsForTable(
|
||||
mobileLogger,
|
||||
columnPrefix = "",
|
||||
columnName = "dataEnabled",
|
||||
initialValue = initial,
|
||||
initialValue = initial
|
||||
)
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), initial)
|
||||
}
|
||||
|
||||
private fun dataConnectionAllowed(): Boolean = telephonyManager.isDataConnectionAllowed
|
||||
|
||||
class Factory
|
||||
@Inject
|
||||
constructor(
|
||||
@@ -299,7 +313,7 @@ class MobileConnectionRepositoryImpl(
|
||||
private val context: Context,
|
||||
private val telephonyManager: TelephonyManager,
|
||||
private val logger: ConnectivityPipelineLogger,
|
||||
private val globalSettings: GlobalSettings,
|
||||
private val carrierConfigRepository: CarrierConfigRepository,
|
||||
private val mobileMappingsProxy: MobileMappingsProxy,
|
||||
@Background private val bgDispatcher: CoroutineDispatcher,
|
||||
@Application private val scope: CoroutineScope,
|
||||
@@ -309,7 +323,6 @@ class MobileConnectionRepositoryImpl(
|
||||
mobileLogger: TableLogBuffer,
|
||||
defaultNetworkName: NetworkNameModel,
|
||||
networkNameSeparator: String,
|
||||
globalMobileDataSettingChangedEvent: Flow<Unit>,
|
||||
): MobileConnectionRepository {
|
||||
return MobileConnectionRepositoryImpl(
|
||||
context,
|
||||
@@ -317,9 +330,8 @@ class MobileConnectionRepositoryImpl(
|
||||
defaultNetworkName,
|
||||
networkNameSeparator,
|
||||
telephonyManager.createForSubscriptionId(subId),
|
||||
globalSettings,
|
||||
carrierConfigRepository.getOrCreateConfigForSubId(subId),
|
||||
broadcastDispatcher,
|
||||
globalMobileDataSettingChangedEvent,
|
||||
mobileMappingsProxy,
|
||||
bgDispatcher,
|
||||
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
|
||||
}
|
||||
|
||||
@@ -19,14 +19,12 @@ package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.IntentFilter
|
||||
import android.database.ContentObserver
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.ConnectivityManager.NetworkCallback
|
||||
import android.net.Network
|
||||
import android.net.NetworkCapabilities
|
||||
import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
|
||||
import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
|
||||
import android.provider.Settings.Global.MOBILE_DATA
|
||||
import android.telephony.CarrierConfigManager
|
||||
import android.telephony.SubscriptionInfo
|
||||
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.repository.WifiRepository
|
||||
import com.android.systemui.util.kotlin.pairwise
|
||||
import com.android.systemui.util.settings.GlobalSettings
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -87,7 +84,6 @@ constructor(
|
||||
private val logger: ConnectivityPipelineLogger,
|
||||
mobileMappingsProxy: MobileMappingsProxy,
|
||||
broadcastDispatcher: BroadcastDispatcher,
|
||||
private val globalSettings: GlobalSettings,
|
||||
private val context: Context,
|
||||
@Background private val bgDispatcher: CoroutineDispatcher,
|
||||
@Application private val scope: CoroutineScope,
|
||||
@@ -222,29 +218,6 @@ constructor(
|
||||
?: 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")
|
||||
override val defaultMobileNetworkConnectivity: StateFlow<MobileConnectivityModel> =
|
||||
conflatedCallbackFlow {
|
||||
@@ -315,7 +288,6 @@ constructor(
|
||||
isCarrierMerged(subId),
|
||||
defaultNetworkName,
|
||||
networkNameSeparator,
|
||||
globalMobileDataSettingChangedEvent,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
const val SB_LOGGING_TAG = "SbConnectivity"
|
||||
|
||||
|
||||
@@ -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) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -71,9 +71,6 @@ class FakeMobileConnectionsRepository(
|
||||
?: FakeMobileConnectionRepository(subId, tableLogBuffer).also { subIdRepos[subId] = it }
|
||||
}
|
||||
|
||||
private val _globalMobileDataSettingChangedEvent = MutableStateFlow(Unit)
|
||||
override val globalMobileDataSettingChangedEvent = _globalMobileDataSettingChangedEvent
|
||||
|
||||
override val defaultDataSubRatConfig = MutableStateFlow(MobileMappings.Config())
|
||||
|
||||
private val _defaultMobileIconMapping = MutableStateFlow(TEST_MAPPING)
|
||||
@@ -94,10 +91,6 @@ class FakeMobileConnectionsRepository(
|
||||
_mobileConnectivity.value = model
|
||||
}
|
||||
|
||||
suspend fun triggerGlobalMobileDataSettingChangedEvent() {
|
||||
_globalMobileDataSettingChangedEvent.emit(Unit)
|
||||
}
|
||||
|
||||
fun setActiveMobileDataSubscriptionId(subId: Int) {
|
||||
_activeMobileDataSubscriptionId.value = subId
|
||||
}
|
||||
|
||||
@@ -40,7 +40,6 @@ import com.android.systemui.util.mockito.any
|
||||
import com.android.systemui.util.mockito.kotlinArgumentCaptor
|
||||
import com.android.systemui.util.mockito.mock
|
||||
import com.android.systemui.util.mockito.whenever
|
||||
import com.android.systemui.util.settings.FakeSettings
|
||||
import com.android.systemui.util.time.FakeSystemClock
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -85,7 +84,6 @@ class MobileRepositorySwitcherTest : SysuiTestCase() {
|
||||
@Mock private lateinit var demoModeController: DemoModeController
|
||||
@Mock private lateinit var dumpManager: DumpManager
|
||||
|
||||
private val globalSettings = FakeSettings()
|
||||
private val fakeNetworkEventsFlow = MutableStateFlow<FakeNetworkEventModel?>(null)
|
||||
private val mobileMappings = FakeMobileMappingsProxy()
|
||||
|
||||
@@ -118,7 +116,6 @@ class MobileRepositorySwitcherTest : SysuiTestCase() {
|
||||
logger,
|
||||
mobileMappings,
|
||||
fakeBroadcastDispatcher,
|
||||
globalSettings,
|
||||
context,
|
||||
IMMEDIATE,
|
||||
scope,
|
||||
|
||||
@@ -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.NetworkNameModel
|
||||
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.util.FakeMobileMappingsProxy
|
||||
import com.android.systemui.util.mockito.any
|
||||
import com.android.systemui.util.mockito.eq
|
||||
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.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
@@ -57,22 +54,15 @@ class FullMobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
|
||||
private val testDispatcher = UnconfinedTestDispatcher()
|
||||
private val testScope = TestScope(testDispatcher)
|
||||
private val mobileMappings = FakeMobileMappingsProxy()
|
||||
private val tableLogBuffer = mock<TableLogBuffer>()
|
||||
private val mobileFactory = mock<MobileConnectionRepositoryImpl.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 carrierMergedRepo: FakeMobileConnectionRepository
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
connectionsRepo = FakeMobileConnectionsRepository(mobileMappings, tableLogBuffer)
|
||||
|
||||
mobileRepo = FakeMobileConnectionRepository(SUB_ID, tableLogBuffer)
|
||||
carrierMergedRepo = FakeMobileConnectionRepository(SUB_ID, tableLogBuffer)
|
||||
|
||||
@@ -82,7 +72,6 @@ class FullMobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
any(),
|
||||
eq(DEFAULT_NAME),
|
||||
eq(SEP),
|
||||
eq(globalMobileDataSettingChangedEvent),
|
||||
)
|
||||
)
|
||||
.thenReturn(mobileRepo)
|
||||
@@ -109,7 +98,6 @@ class FullMobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
tableLogBuffer,
|
||||
DEFAULT_NAME,
|
||||
SEP,
|
||||
globalMobileDataSettingChangedEvent
|
||||
)
|
||||
}
|
||||
|
||||
@@ -310,7 +298,6 @@ class FullMobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
startingIsCarrierMerged = false,
|
||||
DEFAULT_NAME,
|
||||
SEP,
|
||||
globalMobileDataSettingChangedEvent,
|
||||
)
|
||||
|
||||
val connection1Repeat =
|
||||
@@ -319,7 +306,6 @@ class FullMobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
startingIsCarrierMerged = false,
|
||||
DEFAULT_NAME,
|
||||
SEP,
|
||||
globalMobileDataSettingChangedEvent,
|
||||
)
|
||||
|
||||
assertThat(connection1.tableLogBuffer)
|
||||
@@ -345,7 +331,6 @@ class FullMobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
startingIsCarrierMerged = false,
|
||||
DEFAULT_NAME,
|
||||
SEP,
|
||||
globalMobileDataSettingChangedEvent,
|
||||
)
|
||||
|
||||
// WHEN a connection with the same sub ID but carrierMerged = true is created
|
||||
@@ -355,7 +340,6 @@ class FullMobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
startingIsCarrierMerged = true,
|
||||
DEFAULT_NAME,
|
||||
SEP,
|
||||
globalMobileDataSettingChangedEvent,
|
||||
)
|
||||
|
||||
// THEN the same table is re-used
|
||||
@@ -374,7 +358,6 @@ class FullMobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
tableLogBuffer,
|
||||
DEFAULT_NAME,
|
||||
SEP,
|
||||
globalMobileDataSettingChangedEvent,
|
||||
testScope.backgroundScope,
|
||||
mobileFactory,
|
||||
carrierMergedFactory,
|
||||
|
||||
@@ -17,8 +17,7 @@
|
||||
package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.UserHandle
|
||||
import android.provider.Settings
|
||||
import android.telephony.CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL
|
||||
import android.telephony.CellSignalStrengthCdma
|
||||
import android.telephony.NetworkRegistrationInfo
|
||||
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.OverrideNetworkType
|
||||
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.repository.FakeMobileConnectionsRepository
|
||||
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.mock
|
||||
import com.android.systemui.util.mockito.whenever
|
||||
import com.android.systemui.util.settings.FakeSettings
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -101,12 +102,15 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
|
||||
private val scope = CoroutineScope(IMMEDIATE)
|
||||
private val mobileMappings = FakeMobileMappingsProxy()
|
||||
private val globalSettings = FakeSettings()
|
||||
private val systemUiCarrierConfig =
|
||||
SystemUiCarrierConfig(
|
||||
SUB_1_ID,
|
||||
createTestConfig(),
|
||||
)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
globalSettings.userId = UserHandle.USER_ALL
|
||||
whenever(telephonyManager.subscriptionId).thenReturn(SUB_1_ID)
|
||||
|
||||
connectionsRepo = FakeMobileConnectionsRepository(mobileMappings, tableLogger)
|
||||
@@ -118,9 +122,8 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
DEFAULT_NAME,
|
||||
SEP,
|
||||
telephonyManager,
|
||||
globalSettings,
|
||||
systemUiCarrierConfig,
|
||||
fakeBroadcastDispatcher,
|
||||
connectionsRepo.globalMobileDataSettingChangedEvent,
|
||||
mobileMappings,
|
||||
IMMEDIATE,
|
||||
logger,
|
||||
@@ -399,53 +402,27 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
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) {
|
||||
whenever(telephonyManager.isDataConnectionAllowed).thenReturn(false)
|
||||
val job = underTest.dataEnabled.launchIn(this)
|
||||
|
||||
assertThat(underTest.dataEnabled.value).isFalse()
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isDataConnectionAllowed_subIdSettingUpdate_valueUpdated() =
|
||||
fun `is data enabled - tracks telephony callback`() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
val subIdSettingName = "${Settings.Global.MOBILE_DATA}$SUB_1_ID"
|
||||
|
||||
var latest: Boolean? = null
|
||||
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)
|
||||
globalSettings.putInt(subIdSettingName, 0)
|
||||
assertThat(latest).isFalse()
|
||||
assertThat(underTest.dataEnabled.value).isFalse()
|
||||
|
||||
whenever(telephonyManager.isDataConnectionAllowed).thenReturn(true)
|
||||
globalSettings.putInt(subIdSettingName, 1)
|
||||
val callback = getTelephonyCallbackForType<TelephonyCallback.DataEnabledListener>()
|
||||
|
||||
callback.onDataEnabledChanged(true, 1)
|
||||
assertThat(latest).isTrue()
|
||||
|
||||
whenever(telephonyManager.isDataConnectionAllowed).thenReturn(false)
|
||||
globalSettings.putInt(subIdSettingName, 0)
|
||||
callback.onDataEnabledChanged(false, 1)
|
||||
assertThat(latest).isFalse()
|
||||
|
||||
job.cancel()
|
||||
@@ -466,8 +443,6 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
fun `roaming - cdma - queries telephony manager`() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
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 cb = getTelephonyCallbackForType<ServiceStateListener>()
|
||||
@@ -487,7 +462,6 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
|
||||
assertThat(latest).isTrue()
|
||||
|
||||
telephonyJob.cancel()
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@@ -673,6 +647,29 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
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> {
|
||||
val callbackCaptor = argumentCaptor<TelephonyCallback>()
|
||||
Mockito.verify(telephonyManager).registerTelephonyCallback(any(), callbackCaptor.capture())
|
||||
|
||||
@@ -23,7 +23,6 @@ import android.net.NetworkCapabilities
|
||||
import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
|
||||
import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
|
||||
import android.os.ParcelUuid
|
||||
import android.provider.Settings
|
||||
import android.telephony.CarrierConfigManager
|
||||
import android.telephony.SubscriptionInfo
|
||||
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.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
|
||||
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.util.FakeMobileMappingsProxy
|
||||
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.mock
|
||||
import com.android.systemui.util.mockito.whenever
|
||||
import com.android.systemui.util.settings.FakeSettings
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import java.util.UUID
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -80,6 +79,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
|
||||
private lateinit var carrierMergedFactory: CarrierMergedConnectionRepository.Factory
|
||||
private lateinit var fullConnectionFactory: FullMobileConnectionRepository.Factory
|
||||
private lateinit var wifiRepository: FakeWifiRepository
|
||||
private lateinit var carrierConfigRepository: CarrierConfigRepository
|
||||
@Mock private lateinit var connectivityManager: ConnectivityManager
|
||||
@Mock private lateinit var subscriptionManager: SubscriptionManager
|
||||
@Mock private lateinit var telephonyManager: TelephonyManager
|
||||
@@ -89,7 +89,6 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
|
||||
private val mobileMappings = FakeMobileMappingsProxy()
|
||||
|
||||
private val scope = CoroutineScope(IMMEDIATE)
|
||||
private val globalSettings = FakeSettings()
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
@@ -119,16 +118,25 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
|
||||
|
||||
wifiRepository = FakeWifiRepository()
|
||||
|
||||
carrierConfigRepository =
|
||||
CarrierConfigRepository(
|
||||
fakeBroadcastDispatcher,
|
||||
mock(),
|
||||
mock(),
|
||||
logger,
|
||||
scope,
|
||||
)
|
||||
|
||||
connectionFactory =
|
||||
MobileConnectionRepositoryImpl.Factory(
|
||||
fakeBroadcastDispatcher,
|
||||
context = context,
|
||||
telephonyManager = telephonyManager,
|
||||
bgDispatcher = IMMEDIATE,
|
||||
globalSettings = globalSettings,
|
||||
logger = logger,
|
||||
mobileMappingsProxy = mobileMappings,
|
||||
scope = scope,
|
||||
carrierConfigRepository = carrierConfigRepository,
|
||||
)
|
||||
carrierMergedFactory =
|
||||
CarrierMergedConnectionRepository.Factory(
|
||||
@@ -151,7 +159,6 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
|
||||
logger,
|
||||
mobileMappings,
|
||||
fakeBroadcastDispatcher,
|
||||
globalSettings,
|
||||
context,
|
||||
IMMEDIATE,
|
||||
scope,
|
||||
@@ -543,24 +550,6 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
|
||||
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
|
||||
fun mobileConnectivity_isConnected_isNotValidated() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
@@ -629,7 +618,6 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
|
||||
logger,
|
||||
mobileMappings,
|
||||
fakeBroadcastDispatcher,
|
||||
globalSettings,
|
||||
context,
|
||||
IMMEDIATE,
|
||||
scope,
|
||||
|
||||
Reference in New Issue
Block a user