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

Commit 74daba5a authored by Evan Laird's avatar Evan Laird Committed by Android (Google) Code Review
Browse files

Merge changes I1c9ade7e,I06365d19,I0945a29d,I8e798197,Ib3a4f14b, ... into tm-qpr-dev

* changes:
  [Sb refactor] [demo] Use better default for demo mode
  [Sb refactor] Support NOT_DEFAULT_DATA network type
  Partial revert of "[SB Refactor] Remove unused `isDefaultDataSubscription` flow."
  [Sb refactor] Implement 2s grace period for data switching
  [Sb refactor] Add the default network's connectivity to the view model
  [Sb refactor] Upgrade MobileIconsInteractorTest to use testScope
parents cbb432e3 4c85530d
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ 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
import com.android.settingslib.mobile.MobileMappings
import com.android.settingslib.mobile.MobileMappings.Config
@@ -37,6 +38,15 @@ interface MobileConnectionsRepository {
    /** Observable for the subscriptionId of the current mobile data connection */
    val activeMobileDataSubscriptionId: StateFlow<Int>

    /**
     * Observable event for when the active data sim switches but the group stays the same. E.g.,
     * CBRS switching would trigger this
     */
    val activeSubChangedInGroupEvent: Flow<Unit>

    /** Tracks [SubscriptionManager.getDefaultDataSubscriptionId] */
    val defaultDataSubId: StateFlow<Int>

    /** The current connectivity status for the default mobile network connection */
    val defaultMobileNetworkConnectivity: StateFlow<MobileConnectivityModel>

+8 −0
Original line number Diff line number Diff line
@@ -124,6 +124,9 @@ constructor(
                realRepository.activeMobileDataSubscriptionId.value
            )

    override val activeSubChangedInGroupEvent: Flow<Unit> =
        activeRepo.flatMapLatest { it.activeSubChangedInGroupEvent }

    override val defaultDataSubRatConfig: StateFlow<MobileMappings.Config> =
        activeRepo
            .flatMapLatest { it.defaultDataSubRatConfig }
@@ -139,6 +142,11 @@ constructor(
    override val defaultMobileIconGroup: Flow<SignalIcon.MobileIconGroup> =
        activeRepo.flatMapLatest { it.defaultMobileIconGroup }

    override val defaultDataSubId: StateFlow<Int> =
        activeRepo
            .flatMapLatest { it.defaultDataSubId }
            .stateIn(scope, SharingStarted.WhileSubscribed(), realRepository.defaultDataSubId.value)

    override val defaultMobileNetworkConnectivity: StateFlow<MobileConnectivityModel> =
        activeRepo
            .flatMapLatest { it.defaultMobileNetworkConnectivity }
+12 −1
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@ import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
@@ -120,6 +121,9 @@ constructor(
                subscriptions.value.firstOrNull()?.subscriptionId ?: INVALID_SUBSCRIPTION_ID
            )

    // TODO(b/261029387): consider adding a demo command for this
    override val activeSubChangedInGroupEvent: Flow<Unit> = flowOf()

    /** Demo mode doesn't currently support modifications to the mobile mappings */
    override val defaultDataSubRatConfig =
        MutableStateFlow(MobileMappings.Config.readConfig(context))
@@ -148,8 +152,12 @@ constructor(

    private fun <K, V> Map<K, V>.reverse() = entries.associateBy({ it.value }) { it.key }

    // TODO(b/261029387): add a command for this value
    override val defaultDataSubId = MutableStateFlow(INVALID_SUBSCRIPTION_ID)

    // TODO(b/261029387): not yet supported
    override val defaultMobileNetworkConnectivity = MutableStateFlow(MobileConnectivityModel())
    override val defaultMobileNetworkConnectivity =
        MutableStateFlow(MobileConnectivityModel(isConnected = true, isValidated = true))

    override fun getRepoForSubId(subId: Int): DemoMobileConnectionRepository {
        val current = connectionRepoCache[subId]?.repo
@@ -229,6 +237,9 @@ constructor(
        val connection = getRepoForSubId(subId)
        connectionRepoCache[subId]?.lastMobileState = state

        // TODO(b/261029387): until we have a command, use the most recent subId
        defaultDataSubId.value = subId

        // This is always true here, because we split out disabled states at the data-source level
        connection.dataEnabled.value = true
        connection.networkName.value = NetworkNameModel.Derived(state.name)
+51 −3
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import android.telephony.TelephonyCallback
import android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener
import android.telephony.TelephonyManager
import androidx.annotation.VisibleForTesting
import com.android.internal.telephony.PhoneConstants
import com.android.settingslib.SignalIcon.MobileIconGroup
import com.android.settingslib.mobile.MobileMappings.Config
import com.android.systemui.R
@@ -52,6 +53,7 @@ import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logInputChange
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.pairwiseBy
import com.android.systemui.util.settings.GlobalSettings
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
@@ -60,9 +62,12 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.asExecutor
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.merge
@@ -159,10 +164,24 @@ constructor(
            .logInputChange(logger, "onActiveDataSubscriptionIdChanged")
            .stateIn(scope, started = SharingStarted.WhileSubscribed(), INVALID_SUBSCRIPTION_ID)

    private val defaultDataSubIdChangedEvent =
    private val defaultDataSubIdChangeEvent: MutableSharedFlow<Unit> =
        MutableSharedFlow(extraBufferCapacity = 1)

    override val defaultDataSubId: StateFlow<Int> =
        broadcastDispatcher
            .broadcastFlow(IntentFilter(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED))
            .broadcastFlow(
                IntentFilter(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)
            ) { intent, _ ->
                intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY, INVALID_SUBSCRIPTION_ID)
            }
            .distinctUntilChanged()
            .logInputChange(logger, "ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED")
            .onEach { defaultDataSubIdChangeEvent.tryEmit(Unit) }
            .stateIn(
                scope,
                SharingStarted.WhileSubscribed(),
                SubscriptionManager.getDefaultDataSubscriptionId()
            )

    private val carrierConfigChangedEvent =
        broadcastDispatcher
@@ -170,7 +189,7 @@ constructor(
            .logInputChange(logger, "ACTION_CARRIER_CONFIG_CHANGED")

    override val defaultDataSubRatConfig: StateFlow<Config> =
        merge(defaultDataSubIdChangedEvent, carrierConfigChangedEvent)
        merge(defaultDataSubIdChangeEvent, carrierConfigChangedEvent)
            .mapLatest { Config.readConfig(context) }
            .distinctUntilChanged()
            .logInputChange(logger, "defaultDataSubRatConfig")
@@ -258,6 +277,35 @@ constructor(
            .logInputChange(logger, "defaultMobileNetworkConnectivity")
            .stateIn(scope, SharingStarted.WhileSubscribed(), MobileConnectivityModel())

    /**
     * Flow that tracks the active mobile data subscriptions. Emits `true` whenever the active data
     * subscription Id changes but the subscription group remains the same. In these cases, we want
     * to retain the previous subscription's validation status for up to 2s to avoid flickering the
     * icon.
     *
     * TODO(b/265164432): we should probably expose all change events, not just same group
     */
    @SuppressLint("MissingPermission")
    override val activeSubChangedInGroupEvent =
        flow {
                activeMobileDataSubscriptionId.pairwiseBy { prevVal: Int, newVal: Int ->
                    if (!defaultMobileNetworkConnectivity.value.isValidated) {
                        return@pairwiseBy
                    }
                    val prevSub = subscriptionManager.getActiveSubscriptionInfo(prevVal)
                    val nextSub = subscriptionManager.getActiveSubscriptionInfo(newVal)

                    if (prevSub == null || nextSub == null) {
                        return@pairwiseBy
                    }

                    if (prevSub.groupUuid != null && prevSub.groupUuid == nextSub.groupUuid) {
                        emit(Unit)
                    }
                }
            }
            .flowOn(bgDispatcher)

    private fun isValidSubId(subId: Int): Boolean {
        subscriptions.value.forEach {
            if (it.subscriptionId == subId) {
+41 −4
Original line number Diff line number Diff line
@@ -18,9 +18,11 @@ package com.android.systemui.statusbar.pipeline.mobile.domain.interactor

import android.telephony.CarrierConfigManager
import com.android.settingslib.SignalIcon.MobileIconGroup
import com.android.settingslib.mobile.TelephonyIcons.NOT_DEFAULT_DATA
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState.Connected
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
@@ -41,12 +43,29 @@ interface MobileIconInteractor {
    /** The current mobile data activity */
    val activity: Flow<DataActivityModel>

    /** Only true if mobile is the default transport but is not validated, otherwise false */
    val isDefaultConnectionFailed: StateFlow<Boolean>
    /**
     * This bit is meant to be `true` if and only if the default network capabilities (see
     * [android.net.ConnectivityManager.registerDefaultNetworkCallback]) result in a network that
     * has the [android.net.NetworkCapabilities.TRANSPORT_CELLULAR] represented.
     *
     * Note that this differs from [isDataConnected], which is tracked by telephony and has to do
     * with the state of using this mobile connection for data as opposed to just voice. It is
     * possible for a mobile subscription to be connected but not be in a connected data state, and
     * thus we wouldn't want to show the network type icon.
     */
    val isConnected: Flow<Boolean>

    /** True when telephony tells us that the data state is CONNECTED */
    /**
     * True when telephony tells us that the data state is CONNECTED. See
     * [android.telephony.TelephonyCallback.DataConnectionStateListener] for more details. We
     * consider this connection to be serving data, and thus want to show a network type icon, when
     * data is connected. Other data connection states would typically cause us not to show the icon
     */
    val isDataConnected: StateFlow<Boolean>

    /** Only true if mobile is the default transport but is not validated, otherwise false */
    val isDefaultConnectionFailed: StateFlow<Boolean>

    /** True if we consider this connection to be in service, i.e. can make calls */
    val isInService: StateFlow<Boolean>

@@ -100,8 +119,10 @@ class MobileIconInteractorImpl(
    defaultSubscriptionHasDataEnabled: StateFlow<Boolean>,
    override val alwaysShowDataRatIcon: StateFlow<Boolean>,
    override val alwaysUseCdmaLevel: StateFlow<Boolean>,
    defaultMobileConnectivity: StateFlow<MobileConnectivityModel>,
    defaultMobileIconMapping: StateFlow<Map<String, MobileIconGroup>>,
    defaultMobileIconGroup: StateFlow<MobileIconGroup>,
    defaultDataSubId: StateFlow<Int>,
    override val isDefaultConnectionFailed: StateFlow<Boolean>,
    connectionRepository: MobileConnectionRepository,
) : MobileIconInteractor {
@@ -111,8 +132,19 @@ class MobileIconInteractorImpl(

    override val activity = connectionInfo.mapLatest { it.dataActivityDirection }

    override val isConnected: Flow<Boolean> = defaultMobileConnectivity.mapLatest { it.isConnected }

    override val isDataEnabled: StateFlow<Boolean> = connectionRepository.dataEnabled

    private val isDefault =
        defaultDataSubId
            .mapLatest { connectionRepository.subId == it }
            .stateIn(
                scope,
                SharingStarted.WhileSubscribed(),
                connectionRepository.subId == defaultDataSubId.value
            )

    override val isDefaultDataEnabled = defaultSubscriptionHasDataEnabled

    override val networkName =
@@ -137,7 +169,12 @@ class MobileIconInteractorImpl(
                connectionInfo,
                defaultMobileIconMapping,
                defaultMobileIconGroup,
            ) { info, mapping, defaultGroup ->
                isDefault,
            ) { info, mapping, defaultGroup, isDefault ->
                if (!isDefault) {
                    return@combine NOT_DEFAULT_DATA
                }

                when (info.resolvedNetworkType) {
                    is ResolvedNetworkType.CarrierMergedNetworkType ->
                        info.resolvedNetworkType.iconGroupOverride
Loading