Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e03d1c36 authored by Evan Laird's avatar Evan Laird
Browse files

[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
parent 1a10e2da
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -269,7 +269,7 @@ filegroup {
        "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",
+5 −0
Original line number Diff line number Diff line
@@ -32,6 +32,8 @@ import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneMod
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
@@ -119,6 +121,9 @@ abstract class StatusBarPipelineModule {

    @Binds abstract fun mobileMappingsProxy(impl: MobileMappingsProxyImpl): MobileMappingsProxy

    @Binds
    abstract fun carrierConfigRepository(impl: CarrierConfigRepositoryImpl): CarrierConfigRepository

    @Binds
    abstract fun subscriptionManagerProxy(
        impl: SubscriptionManagerProxyImpl
+4 −17
Original line number Diff line number Diff line
@@ -48,11 +48,7 @@ import kotlinx.coroutines.flow.asStateFlow
 * 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 @@ internal constructor(
    /** 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 @@ internal constructor(
}

/** 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()

+3 −100
Original line number Diff line number Diff line
@@ -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 @@ import kotlinx.coroutines.flow.shareIn
 *
 * 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
}
+120 −0
Original line number Diff line number Diff line
/*
 * 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")
        }
    }
}
Loading