[Sb refactor] CarrierConfigRepository
CarrierConfigRepository is meant to replace the old CarrierConfigTracker class and be easier to add new keys to. It exposes the ability to get a SystemUiCarrierConfig object which exposes tracked keys as individual Flows to be consumed in the modern-arch way. Test: CarrierConfigRepositoryTest Test: SystemUiCarrierConfigTest Test: MobileConnectionRepositoryTest Bug: 238425913 Change-Id: Id3ae744f62aae526bdd8573151632814617c1bd7
This commit is contained in:
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,8 +44,10 @@ 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
|
||||
@@ -63,7 +65,7 @@ import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
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
|
||||
@@ -82,6 +84,7 @@ class MobileConnectionRepositoryImpl(
|
||||
networkNameSeparator: String,
|
||||
private val telephonyManager: TelephonyManager,
|
||||
private val globalSettings: GlobalSettings,
|
||||
systemUiCarrierConfig: SystemUiCarrierConfig,
|
||||
broadcastDispatcher: BroadcastDispatcher,
|
||||
globalMobileDataSettingChangedEvent: Flow<Unit>,
|
||||
mobileMappingsProxy: MobileMappingsProxy,
|
||||
@@ -216,10 +219,15 @@ class MobileConnectionRepositoryImpl(
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), state)
|
||||
}
|
||||
|
||||
// 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)
|
||||
override val numberOfLevels =
|
||||
systemUiCarrierConfig.shouldInflateSignalStrength
|
||||
.map { shouldInflate ->
|
||||
if (shouldInflate) {
|
||||
DEFAULT_NUM_LEVELS + 1
|
||||
} else {
|
||||
DEFAULT_NUM_LEVELS
|
||||
}
|
||||
}
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), DEFAULT_NUM_LEVELS)
|
||||
|
||||
/** Produces whenever the mobile data setting changes for this subId */
|
||||
@@ -300,6 +308,7 @@ class MobileConnectionRepositoryImpl(
|
||||
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,
|
||||
@@ -318,6 +327,7 @@ class MobileConnectionRepositoryImpl(
|
||||
networkNameSeparator,
|
||||
telephonyManager.createForSubscriptionId(subId),
|
||||
globalSettings,
|
||||
carrierConfigRepository.getOrCreateConfigForSubId(subId),
|
||||
broadcastDispatcher,
|
||||
globalMobileDataSettingChangedEvent,
|
||||
mobileMappingsProxy,
|
||||
|
||||
@@ -231,6 +231,15 @@ constructor(
|
||||
)
|
||||
}
|
||||
|
||||
fun logCarrierConfigChanged(subId: Int) {
|
||||
buffer.log(
|
||||
SB_LOGGING_TAG,
|
||||
LogLevel.INFO,
|
||||
{ int1 = subId },
|
||||
{ "onCarrierConfigChanged: subId=$int1" },
|
||||
)
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,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
|
||||
@@ -59,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
|
||||
@@ -100,6 +104,11 @@ 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() {
|
||||
@@ -117,6 +126,7 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
SEP,
|
||||
telephonyManager,
|
||||
globalSettings,
|
||||
systemUiCarrierConfig,
|
||||
fakeBroadcastDispatcher,
|
||||
connectionsRepo.globalMobileDataSettingChangedEvent,
|
||||
mobileMappings,
|
||||
@@ -625,6 +635,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())
|
||||
|
||||
@@ -39,6 +39,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
|
||||
@@ -80,6 +81,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
|
||||
@@ -119,6 +121,15 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
|
||||
|
||||
wifiRepository = FakeWifiRepository()
|
||||
|
||||
carrierConfigRepository =
|
||||
CarrierConfigRepository(
|
||||
fakeBroadcastDispatcher,
|
||||
mock(),
|
||||
mock(),
|
||||
logger,
|
||||
scope,
|
||||
)
|
||||
|
||||
connectionFactory =
|
||||
MobileConnectionRepositoryImpl.Factory(
|
||||
fakeBroadcastDispatcher,
|
||||
@@ -129,6 +140,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
|
||||
logger = logger,
|
||||
mobileMappingsProxy = mobileMappings,
|
||||
scope = scope,
|
||||
carrierConfigRepository = carrierConfigRepository,
|
||||
)
|
||||
carrierMergedFactory =
|
||||
CarrierMergedConnectionRepository.Factory(
|
||||
|
||||
Reference in New Issue
Block a user