[mobile] split out CarrierConfigRepository interface for testing

Makes it easier to mock out in Kosmos. Then I went ahead and added it to
kosmos.

Bug: 364360986
Flag: EXEMPT refactor
Test: CarrierConfigRepositoryImplTest
Test: MobileConnectionsRepositoryTest
Test: all other sysui tests
Change-Id: I86caeb60e9036a202eced6f05ec69ad104c6aa45
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 3d250fd..a91e1ed 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -269,7 +269,7 @@
         "tests/src/**/systemui/stylus/StylusManagerTest.kt",
         "tests/src/**/systemui/recents/OverviewProxyServiceTest.kt",
         "tests/src/**/systemui/DisplayCutoutBaseViewTest.kt",
-        "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt",
+        "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryImplTest.kt",
         "tests/src/**/systemui/statusbar/policy/BatteryControllerTest.java",
         "tests/src/**/systemui/statusbar/policy/SensitiveNotificationProtectionControllerTest.kt",
         "tests/src/**/systemui/statusbar/KeyboardShortcutsReceiverTest.java",
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
index bfdc8bd..a88b74c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
@@ -32,6 +32,8 @@
 import com.android.systemui.statusbar.pipeline.icons.shared.BindableIconsRegistry
 import com.android.systemui.statusbar.pipeline.icons.shared.BindableIconsRegistryImpl
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.CarrierConfigCoreStartable
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.CarrierConfigRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.CarrierConfigRepositoryImpl
 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.domain.interactor.MobileIconsInteractor
@@ -120,6 +122,9 @@
     @Binds abstract fun mobileMappingsProxy(impl: MobileMappingsProxyImpl): MobileMappingsProxy
 
     @Binds
+    abstract fun carrierConfigRepository(impl: CarrierConfigRepositoryImpl): CarrierConfigRepository
+
+    @Binds
     abstract fun subscriptionManagerProxy(
         impl: SubscriptionManagerProxyImpl
     ): SubscriptionManagerProxy
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfig.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfig.kt
index 0871c86..5f33a754 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfig.kt
@@ -48,11 +48,7 @@
  * 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,
-) {
+class SystemUiCarrierConfig constructor(val subId: Int, defaultConfig: PersistableBundle) {
     @VisibleForTesting
     var isUsingDefault = true
         private set
@@ -67,17 +63,11 @@
     /** Flow tracking the [KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL] config */
     val showOperatorNameInStatusBar: StateFlow<Boolean> = showOperatorName.config
 
-    private val showNetworkSlice =
-        BooleanCarrierConfig(KEY_SHOW_5G_SLICE_ICON_BOOL, defaultConfig)
+    private val showNetworkSlice = BooleanCarrierConfig(KEY_SHOW_5G_SLICE_ICON_BOOL, defaultConfig)
     /** Flow tracking the [KEY_SHOW_5G_SLICE_ICON_BOOL] config */
     val allowNetworkSliceIndicator: StateFlow<Boolean> = showNetworkSlice.config
 
-    private val trackedConfigs =
-        listOf(
-            inflateSignalStrength,
-            showOperatorName,
-            showNetworkSlice,
-        )
+    private val trackedConfigs = listOf(inflateSignalStrength, showOperatorName, showNetworkSlice)
 
     /** Ingest a new carrier config, and switch all of the tracked keys over to the new values */
     fun processNewCarrierConfig(config: PersistableBundle) {
@@ -98,10 +88,7 @@
 }
 
 /** Extracts [key] from the carrier config, and stores it in a flow */
-private class BooleanCarrierConfig(
-    val key: String,
-    defaultConfig: PersistableBundle,
-) {
+private class BooleanCarrierConfig(val key: String, defaultConfig: PersistableBundle) {
     private val _configValue = MutableStateFlow(defaultConfig.getBoolean(key))
     val config = _configValue.asStateFlow()
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepository.kt
index 016ba5f..30c529a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepository.kt
@@ -16,31 +16,8 @@
 
 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.MobileInputLogger
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig
-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
@@ -50,87 +27,13 @@
  *
  * 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: MobileInputLogger,
-    @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())
-
+interface CarrierConfigRepository {
     /**
      * 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)
-    }
+    suspend fun startObservingCarrierConfigUpdates()
 
     /** 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")
-        }
-    }
+    fun getOrCreateConfigForSubId(subId: Int): SystemUiCarrierConfig
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryImpl.kt
new file mode 100644
index 0000000..7ed6b05
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryImpl.kt
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2024 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.MobileInputLogger
+import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig
+import java.io.PrintWriter
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.flow.onEach
+
+@SysUISingleton
+class CarrierConfigRepositoryImpl
+@Inject
+constructor(
+    broadcastDispatcher: BroadcastDispatcher,
+    private val carrierConfigManager: CarrierConfigManager?,
+    dumpManager: DumpManager,
+    logger: MobileInputLogger,
+    @Application scope: CoroutineScope,
+) : CarrierConfigRepository, 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: Flow<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 }
+            }
+
+    override 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)
+    }
+
+    override 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")
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryImplTest.kt
similarity index 96%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryImplTest.kt
index 320c148..34e06d5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryImplTest.kt
@@ -48,11 +48,11 @@
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
-class CarrierConfigRepositoryTest : SysuiTestCase() {
+class CarrierConfigRepositoryImplTest : SysuiTestCase() {
     private val testDispatcher = UnconfinedTestDispatcher()
     private val testScope = TestScope(testDispatcher)
 
-    private lateinit var underTest: CarrierConfigRepository
+    private lateinit var underTest: CarrierConfigRepositoryImpl
     private lateinit var mockitoSession: MockitoSession
     private lateinit var carrierConfigCoreStartable: CarrierConfigCoreStartable
 
@@ -81,7 +81,7 @@
         }
 
         underTest =
-            CarrierConfigRepository(
+            CarrierConfigRepositoryImpl(
                 fakeBroadcastDispatcher,
                 carrierConfigManager,
                 dumpManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index 2e0b7c6..d7456df 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -58,6 +58,7 @@
 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.MobileConnectionRepository
+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.mobile.util.FakeSubscriptionManagerProxy
@@ -68,7 +69,6 @@
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl
 import com.android.systemui.testKosmos
 import com.android.systemui.user.data.repository.fakeUserRepository
-import com.android.systemui.user.data.repository.userRepository
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.capture
@@ -209,14 +209,7 @@
                 wifiTableLogBuffer,
             )
 
-        carrierConfigRepository =
-            CarrierConfigRepository(
-                fakeBroadcastDispatcher,
-                mock(),
-                mock(),
-                logger,
-                testScope.backgroundScope,
-            )
+        carrierConfigRepository = kosmos.carrierConfigRepository
 
         connectionFactory =
             MobileConnectionRepositoryImpl.Factory(
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryKosmos.kt
new file mode 100644
index 0000000..a6431af
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryKosmos.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 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.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+
+val Kosmos.carrierConfigRepository: CarrierConfigRepository by Fixture {
+    FakeCarrierConfigRepository()
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeCarrierConfigRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeCarrierConfigRepository.kt
new file mode 100644
index 0000000..e0ac9c8
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeCarrierConfigRepository.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2024 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.os.PersistableBundle
+import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig
+
+class FakeCarrierConfigRepository : CarrierConfigRepository {
+    override suspend fun startObservingCarrierConfigUpdates() {}
+
+    val configsById = mutableMapOf<Int, SystemUiCarrierConfig>()
+
+    override fun getOrCreateConfigForSubId(subId: Int): SystemUiCarrierConfig =
+        configsById.getOrPut(subId) { SystemUiCarrierConfig(subId, createDefaultTestConfig()) }
+}
+
+fun createDefaultTestConfig() =
+    PersistableBundle().also {
+        it.putBoolean(
+            android.telephony.CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL,
+            false,
+        )
+        it.putBoolean(
+            android.telephony.CarrierConfigManager.KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL,
+            false,
+        )
+        it.putBoolean(android.telephony.CarrierConfigManager.KEY_SHOW_5G_SLICE_ICON_BOOL, true)
+    }
+
+/** Override the default config with the given (key, value) pair */
+fun configWithOverride(key: String, override: Boolean): PersistableBundle =
+    createDefaultTestConfig().also { it.putBoolean(key, override) }
+
+/** Override any number of configs from the default */
+fun configWithOverrides(vararg overrides: Pair<String, Boolean>) =
+    createDefaultTestConfig().also { config ->
+        overrides.forEach { (key, value) -> config.putBoolean(key, value) }
+    }