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

Commit 7f894afe authored by Evan Laird's avatar Evan Laird
Browse files

[Sb refactor] Implement 2s grace period for data switching

The old mobile pipeline supported this particular use case. Given that
there are 2 mobile subscriptions, A and B, then when data switches from
A to B, we will consider B to be validated (see NetworkCapabilities) for
up to 2 seconds under the conditions:

1. A was validated before the switch
2. A and B are both in the same group (i.e.  they have the same group UUID)

This CL implements the same exact criteria by monitoring for data
switching in the same group and maintaining a validated state for 2
seconds for the default subscription.

Test: MobileIconsInteractorTest
Bug: 238425913
Change-Id: I8e798197beda562634c137377145dea510cbda4c
parent 5ceb454c
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -37,6 +37,12 @@ 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>

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

+3 −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 }
+4 −0
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))
+32 −0
Original line number Diff line number Diff line
@@ -52,6 +52,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
@@ -63,6 +64,8 @@ import kotlinx.coroutines.flow.Flow
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
@@ -258,6 +261,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 −1
Original line number Diff line number Diff line
@@ -32,14 +32,17 @@ import com.android.systemui.util.CarrierConfigTracker
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.transformLatest

/**
 * Business layer logic for the set of mobile subscription icons.
@@ -163,8 +166,45 @@ constructor(
            }
        }

    /**
     * Copied from the old pipeline. We maintain a 2s period of time where we will keep the
     * validated bit from the old active network (A) while data is changing to the new one (B).
     *
     * This condition only applies if
     * 1. A and B are in the same subscription group (e.c. for CBRS data switching) and
     * 2. A was validated before the switch
     *
     * The goal of this is to minimize the flickering in the UI of the cellular indicator
     */
    private val forcingCellularValidation =
        mobileConnectionsRepo.activeSubChangedInGroupEvent
            .filter { mobileConnectionsRepo.defaultMobileNetworkConnectivity.value.isValidated }
            .transformLatest {
                emit(true)
                delay(2000)
                emit(false)
            }
            .stateIn(scope, SharingStarted.WhileSubscribed(), false)

    override val defaultMobileNetworkConnectivity: StateFlow<MobileConnectivityModel> =
        mobileConnectionsRepo.defaultMobileNetworkConnectivity
        combine(
                mobileConnectionsRepo.defaultMobileNetworkConnectivity,
                forcingCellularValidation,
            ) { networkConnectivity, forceValidation ->
                return@combine if (forceValidation) {
                    MobileConnectivityModel(
                        isValidated = true,
                        isConnected = networkConnectivity.isConnected
                    )
                } else {
                    networkConnectivity
                }
            }
            .stateIn(
                scope,
                SharingStarted.WhileSubscribed(),
                mobileConnectionsRepo.defaultMobileNetworkConnectivity.value
            )

    /**
     * Mapping from network type to [MobileIconGroup] using the config generated for the default
Loading