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

Commit 5cbcba28 authored by Evan Laird's avatar Evan Laird
Browse files

[Sb refactor] Remove MobileConnectionModel

Rationale: MobileConnectionModel was loosely a representative data
object for the colletion of TelephonyCallbacks that we register on a
per-subscription basis. However, this data object only existed in the
boundary between MobileConnectionRepository -> MobileIconInteractor, and
was immediately unboxed into the relevant fields for use in business
logic. This meant that the entire object was being collected even if a
downstream client only relied on one field.

Ultimately, this leads to extra work being done, and extra logs being
written (e.g., DataActivity logs heppning even when the config to show
the activity was false).

The objective of this CL is _only_ to unbox the fields from
MobileConnectionModel, and to represent each field as a separate
property on the connection directly. The underlying implementation still
registers only a single callback with every listener, but the repository
breaks out every callback into its own flow immediately.

Test: test in com.android.systemui.statusbar.pipeline.mobile
Bug: 270300839
Change-Id: I8b62ae3367643143c6f36b689c65ab40e65c4510
parent 0f7e276a
Loading
Loading
Loading
Loading
+14 −2
Original line number Diff line number Diff line
@@ -24,9 +24,11 @@ import android.telephony.TelephonyManager.DATA_HANDOVER_IN_PROGRESS
import android.telephony.TelephonyManager.DATA_SUSPENDED
import android.telephony.TelephonyManager.DATA_UNKNOWN
import android.telephony.TelephonyManager.DataState
import com.android.systemui.log.table.Diffable
import com.android.systemui.log.table.TableRowLogger

/** Internal enum representation of the telephony data connection states */
enum class DataConnectionState {
enum class DataConnectionState : Diffable<DataConnectionState> {
    Connected,
    Connecting,
    Disconnected,
@@ -34,7 +36,17 @@ enum class DataConnectionState {
    Suspended,
    HandoverInProgress,
    Unknown,
    Invalid,
    Invalid;

    override fun logDiffs(prevVal: DataConnectionState, row: TableRowLogger) {
        if (prevVal != this) {
            row.logChange(COL_CONNECTION_STATE, name)
        }
    }

    companion object {
        private const val COL_CONNECTION_STATE = "connectionState"
    }
}

fun @receiver:DataState Int.toDataConnectionType(): DataConnectionState =
+0 −176
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.annotation.IntRange
import android.telephony.CellSignalStrength
import android.telephony.TelephonyCallback.CarrierNetworkListener
import android.telephony.TelephonyCallback.DataActivityListener
import android.telephony.TelephonyCallback.DataConnectionStateListener
import android.telephony.TelephonyCallback.DisplayInfoListener
import android.telephony.TelephonyCallback.ServiceStateListener
import android.telephony.TelephonyCallback.SignalStrengthsListener
import android.telephony.TelephonyDisplayInfo
import android.telephony.TelephonyManager
import androidx.annotation.VisibleForTesting
import com.android.systemui.log.table.Diffable
import com.android.systemui.log.table.TableRowLogger
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState.Disconnected
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel

/**
 * Data class containing all of the relevant information for a particular line of service, known as
 * a Subscription in the telephony world. These models are the result of a single telephony listener
 * which has many callbacks which each modify some particular field on this object.
 *
 * The design goal here is to de-normalize fields from the system into our model fields below. So
 * any new field that needs to be tracked should be copied into this data class rather than
 * threading complex system objects through the pipeline.
 */
data class MobileConnectionModel(
    /** Fields below are from [ServiceStateListener.onServiceStateChanged] */
    val isEmergencyOnly: Boolean = false,
    val isRoaming: Boolean = false,
    /**
     * See [android.telephony.ServiceState.getOperatorAlphaShort], this value is defined as the
     * current registered operator name in short alphanumeric format. In some cases this name might
     * be preferred over other methods of calculating the network name
     */
    val operatorAlphaShort: String? = null,

    /**
     * TODO (b/263167683): Clarify this field
     *
     * This check comes from [com.android.settingslib.Utils.isInService]. It is intended to be a
     * mapping from a ServiceState to a notion of connectivity. Notably, it will consider a
     * connection to be in-service if either the voice registration state is IN_SERVICE or the data
     * registration state is IN_SERVICE and NOT IWLAN.
     */
    val isInService: Boolean = false,

    /** Fields below from [SignalStrengthsListener.onSignalStrengthsChanged] */
    val isGsm: Boolean = false,
    @IntRange(from = 0, to = 4)
    val cdmaLevel: Int = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
    @IntRange(from = 0, to = 4)
    val primaryLevel: Int = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,

    /** Fields below from [DataConnectionStateListener.onDataConnectionStateChanged] */
    val dataConnectionState: DataConnectionState = Disconnected,

    /**
     * Fields below from [DataActivityListener.onDataActivity]. See [TelephonyManager] for the
     * values
     */
    val dataActivityDirection: DataActivityModel =
        DataActivityModel(
            hasActivityIn = false,
            hasActivityOut = false,
        ),

    /** Fields below from [CarrierNetworkListener.onCarrierNetworkChange] */
    val carrierNetworkChangeActive: Boolean = false,

    /** Fields below from [DisplayInfoListener.onDisplayInfoChanged]. */

    /**
     * [resolvedNetworkType] is the [TelephonyDisplayInfo.getOverrideNetworkType] if it exists or
     * [TelephonyDisplayInfo.getNetworkType]. This is used to look up the proper network type icon
     */
    val resolvedNetworkType: ResolvedNetworkType = ResolvedNetworkType.UnknownNetworkType,
) : Diffable<MobileConnectionModel> {
    override fun logDiffs(prevVal: MobileConnectionModel, row: TableRowLogger) {
        if (prevVal.dataConnectionState != dataConnectionState) {
            row.logChange(COL_CONNECTION_STATE, dataConnectionState.name)
        }

        if (prevVal.isEmergencyOnly != isEmergencyOnly) {
            row.logChange(COL_EMERGENCY, isEmergencyOnly)
        }

        if (prevVal.isRoaming != isRoaming) {
            row.logChange(COL_ROAMING, isRoaming)
        }

        if (prevVal.operatorAlphaShort != operatorAlphaShort) {
            row.logChange(COL_OPERATOR, operatorAlphaShort)
        }

        if (prevVal.isInService != isInService) {
            row.logChange(COL_IS_IN_SERVICE, isInService)
        }

        if (prevVal.isGsm != isGsm) {
            row.logChange(COL_IS_GSM, isGsm)
        }

        if (prevVal.cdmaLevel != cdmaLevel) {
            row.logChange(COL_CDMA_LEVEL, cdmaLevel)
        }

        if (prevVal.primaryLevel != primaryLevel) {
            row.logChange(COL_PRIMARY_LEVEL, primaryLevel)
        }

        if (prevVal.dataActivityDirection.hasActivityIn != dataActivityDirection.hasActivityIn) {
            row.logChange(COL_ACTIVITY_DIRECTION_IN, dataActivityDirection.hasActivityIn)
        }

        if (prevVal.dataActivityDirection.hasActivityOut != dataActivityDirection.hasActivityOut) {
            row.logChange(COL_ACTIVITY_DIRECTION_OUT, dataActivityDirection.hasActivityOut)
        }

        if (prevVal.carrierNetworkChangeActive != carrierNetworkChangeActive) {
            row.logChange(COL_CARRIER_NETWORK_CHANGE, carrierNetworkChangeActive)
        }

        if (prevVal.resolvedNetworkType != resolvedNetworkType) {
            row.logChange(COL_RESOLVED_NETWORK_TYPE, resolvedNetworkType.toString())
        }
    }

    override fun logFull(row: TableRowLogger) {
        row.logChange(COL_CONNECTION_STATE, dataConnectionState.name)
        row.logChange(COL_EMERGENCY, isEmergencyOnly)
        row.logChange(COL_ROAMING, isRoaming)
        row.logChange(COL_OPERATOR, operatorAlphaShort)
        row.logChange(COL_IS_IN_SERVICE, isInService)
        row.logChange(COL_IS_GSM, isGsm)
        row.logChange(COL_CDMA_LEVEL, cdmaLevel)
        row.logChange(COL_PRIMARY_LEVEL, primaryLevel)
        row.logChange(COL_ACTIVITY_DIRECTION_IN, dataActivityDirection.hasActivityIn)
        row.logChange(COL_ACTIVITY_DIRECTION_OUT, dataActivityDirection.hasActivityOut)
        row.logChange(COL_CARRIER_NETWORK_CHANGE, carrierNetworkChangeActive)
        row.logChange(COL_RESOLVED_NETWORK_TYPE, resolvedNetworkType.toString())
    }

    @VisibleForTesting
    companion object {
        const val COL_EMERGENCY = "EmergencyOnly"
        const val COL_ROAMING = "Roaming"
        const val COL_OPERATOR = "OperatorName"
        const val COL_IS_IN_SERVICE = "IsInService"
        const val COL_IS_GSM = "IsGsm"
        const val COL_CDMA_LEVEL = "CdmaLevel"
        const val COL_PRIMARY_LEVEL = "PrimaryLevel"
        const val COL_CONNECTION_STATE = "ConnectionState"
        const val COL_ACTIVITY_DIRECTION_IN = "DataActivity.In"
        const val COL_ACTIVITY_DIRECTION_OUT = "DataActivity.Out"
        const val COL_CARRIER_NETWORK_CHANGE = "CarrierNetworkChangeActive"
        const val COL_RESOLVED_NETWORK_TYPE = "NetworkType"
    }
}
+17 −1
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@ package com.android.systemui.statusbar.pipeline.mobile.data.model
import android.telephony.Annotation.NetworkType
import com.android.settingslib.SignalIcon
import com.android.settingslib.mobile.TelephonyIcons
import com.android.systemui.log.table.Diffable
import com.android.systemui.log.table.TableRowLogger
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy

/**
@@ -26,11 +28,19 @@ import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
 * on whether or not the display info contains an override type, we may have to call different
 * methods on [MobileMappingsProxy] to generate an icon lookup key.
 */
sealed interface ResolvedNetworkType {
sealed interface ResolvedNetworkType : Diffable<ResolvedNetworkType> {
    val lookupKey: String

    override fun logDiffs(prevVal: ResolvedNetworkType, row: TableRowLogger) {
        if (prevVal != this) {
            row.logChange(COL_NETWORK_TYPE, this.toString())
        }
    }

    object UnknownNetworkType : ResolvedNetworkType {
        override val lookupKey: String = "unknown"

        override fun toString(): String = "Unknown"
    }

    data class DefaultNetworkType(
@@ -47,5 +57,11 @@ sealed interface ResolvedNetworkType {
        override val lookupKey: String = "cwf"

        val iconGroupOverride: SignalIcon.MobileIconGroup = TelephonyIcons.CARRIER_MERGED_WIFI

        override fun toString(): String = "CarrierMerged"
    }

    companion object {
        private const val COL_NETWORK_TYPE = "networkType"
    }
}
+52 −5
Original line number Diff line number Diff line
@@ -17,11 +17,12 @@
package com.android.systemui.statusbar.pipeline.mobile.data.repository

import android.telephony.SubscriptionInfo
import android.telephony.TelephonyCallback
import android.telephony.TelephonyManager
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
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.shared.data.model.DataActivityModel
import kotlinx.coroutines.flow.StateFlow

/**
@@ -45,11 +46,57 @@ interface MobileConnectionRepository {
     */
    val tableLogBuffer: TableLogBuffer

    /** True if the [android.telephony.ServiceState] says this connection is emergency calls only */
    val isEmergencyOnly: StateFlow<Boolean>

    /** True if [android.telephony.ServiceState] says we are roaming */
    val isRoaming: StateFlow<Boolean>

    /**
     * See [android.telephony.ServiceState.getOperatorAlphaShort], this value is defined as the
     * current registered operator name in short alphanumeric format. In some cases this name might
     * be preferred over other methods of calculating the network name
     */
    val operatorAlphaShort: StateFlow<String?>

    /**
     * TODO (b/263167683): Clarify this field
     *
     * This check comes from [com.android.settingslib.Utils.isInService]. It is intended to be a
     * mapping from a ServiceState to a notion of connectivity. Notably, it will consider a
     * connection to be in-service if either the voice registration state is IN_SERVICE or the data
     * registration state is IN_SERVICE and NOT IWLAN.
     */
    val isInService: StateFlow<Boolean>

    /** True if [android.telephony.SignalStrength] told us that this connection is using GSM */
    val isGsm: StateFlow<Boolean>

    /**
     * There is still specific logic in the pipeline that calls out CDMA level explicitly. This
     * field is not completely orthogonal to [primaryLevel], because CDMA could be primary.
     */
    // @IntRange(from = 0, to = 4)
    val cdmaLevel: StateFlow<Int>

    /** [android.telephony.SignalStrength]'s concept of the overall signal level */
    // @IntRange(from = 0, to = 4)
    val primaryLevel: StateFlow<Int>

    /** The current data connection state. See [DataConnectionState] */
    val dataConnectionState: StateFlow<DataConnectionState>

    /** The current data activity direction. See [DataActivityModel] */
    val dataActivityDirection: StateFlow<DataActivityModel>

    /** True if there is currently a carrier network change in process */
    val carrierNetworkChangeActive: StateFlow<Boolean>

    /**
     * A flow that aggregates all necessary callbacks from [TelephonyCallback] into a single
     * listener + model.
     * [resolvedNetworkType] is the [TelephonyDisplayInfo.getOverrideNetworkType] if it exists or
     * [TelephonyDisplayInfo.getNetworkType]. This is used to look up the proper network type icon
     */
    val connectionInfo: StateFlow<MobileConnectionModel>
    val resolvedNetworkType: StateFlow<ResolvedNetworkType>

    /** The total number of levels. Used with [SignalDrawable]. */
    val numberOfLevels: StateFlow<Int>
+106 −48
Original line number Diff line number Diff line
@@ -17,8 +17,9 @@
package com.android.systemui.statusbar.pipeline.mobile.data.repository.demo

import android.content.Context
import android.telephony.CellSignalStrength
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import android.telephony.TelephonyManager.DATA_ACTIVITY_NONE
import android.telephony.TelephonyManager
import android.util.Log
import com.android.settingslib.SignalIcon
import com.android.settingslib.mobile.MobileMappings
@@ -27,20 +28,18 @@ import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.TableLogBufferFactory
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel
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.model.ResolvedNetworkType.DefaultNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
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.data.repository.MobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model.FakeNetworkEventModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model.FakeNetworkEventModel.Mobile
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model.FakeNetworkEventModel.MobileDisabled
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.CarrierMergedConnectionRepository.Companion.createCarrierMergedConnectionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Factory.Companion.MOBILE_CONNECTION_BUFFER_SIZE
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoModeWifiDataSource
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.model.FakeWifiEventModel
@@ -53,6 +52,7 @@ import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
@@ -237,23 +237,18 @@ constructor(
        }
    }

    private fun processEnabledMobileState(state: Mobile) {
    private fun processEnabledMobileState(event: Mobile) {
        // get or create the connection repo, and set its values
        val subId = state.subId ?: DEFAULT_SUB_ID
        val subId = event.subId ?: DEFAULT_SUB_ID
        maybeCreateSubscription(subId)

        val connection = getRepoForSubId(subId)
        connectionRepoCache[subId]?.lastMobileState = state
        connectionRepoCache[subId]?.lastMobileState = event

        // 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.IntentDerived(state.name)

        connection.cdmaRoaming.value = state.roaming
        connection.connectionInfo.value = state.toMobileConnectionModel()
        connection.processDemoMobileEvent(event, event.dataType.toResolvedNetworkType())
    }

    private fun processCarrierMergedWifiState(event: FakeWifiEventModel.CarrierMerged) {
@@ -272,13 +267,7 @@ constructor(
        defaultDataSubId.value = subId

        val connection = getRepoForSubId(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.IntentDerived(CARRIER_MERGED_NAME)
        connection.numberOfLevels.value = event.numberOfLevels
        connection.cdmaRoaming.value = false
        connection.connectionInfo.value = event.toMobileConnectionModel()
        Log.e("CCS", "output connection info = ${connection.connectionInfo.value}")
        connection.processCarrierMergedEvent(event)
    }

    private fun maybeRemoveSubscription(subId: Int?) {
@@ -332,29 +321,6 @@ constructor(
    private fun subIdsString(): String =
        _subscriptions.value.joinToString(",") { it.subscriptionId.toString() }

    private fun Mobile.toMobileConnectionModel(): MobileConnectionModel {
        return MobileConnectionModel(
            isEmergencyOnly = false, // TODO(b/261029387): not yet supported
            isRoaming = roaming,
            isInService = (level ?: 0) > 0,
            isGsm = false, // TODO(b/261029387): not yet supported
            cdmaLevel = level ?: 0,
            primaryLevel = level ?: 0,
            dataConnectionState =
                DataConnectionState.Connected, // TODO(b/261029387): not yet supported
            dataActivityDirection = (activity ?: DATA_ACTIVITY_NONE).toMobileDataActivityModel(),
            carrierNetworkChangeActive = carrierNetworkChange,
            resolvedNetworkType = dataType.toResolvedNetworkType()
        )
    }

    private fun FakeWifiEventModel.CarrierMerged.toMobileConnectionModel(): MobileConnectionModel {
        return createCarrierMergedConnectionModel(
            this.level,
            activity.toMobileDataActivityModel(),
        )
    }

    private fun SignalIcon.MobileIconGroup?.toResolvedNetworkType(): ResolvedNetworkType {
        val key = mobileMappingsReverseLookup.value[this] ?: "dis"
        return DefaultNetworkType(key)
@@ -364,8 +330,6 @@ constructor(
        private const val TAG = "DemoMobileConnectionsRepo"

        private const val DEFAULT_SUB_ID = 1

        private const val CARRIER_MERGED_NAME = "Carrier Merged Network"
    }
}

@@ -379,13 +343,107 @@ class DemoMobileConnectionRepository(
    override val subId: Int,
    override val tableLogBuffer: TableLogBuffer,
) : MobileConnectionRepository {
    override val connectionInfo = MutableStateFlow(MobileConnectionModel())
    private val _isEmergencyOnly = MutableStateFlow(false)
    override val isEmergencyOnly = _isEmergencyOnly.asStateFlow()

    private val _isRoaming = MutableStateFlow(false)
    override val isRoaming = _isRoaming.asStateFlow()

    private val _operatorAlphaShort: MutableStateFlow<String?> = MutableStateFlow(null)
    override val operatorAlphaShort = _operatorAlphaShort.asStateFlow()

    private val _isInService = MutableStateFlow(false)
    override val isInService = _isInService.asStateFlow()

    private val _isGsm = MutableStateFlow(false)
    override val isGsm = _isGsm.asStateFlow()

    override val numberOfLevels = MutableStateFlow(DEFAULT_NUM_LEVELS)
    private val _cdmaLevel = MutableStateFlow(CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN)
    override val cdmaLevel = _cdmaLevel.asStateFlow()

    private val _primaryLevel = MutableStateFlow(CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN)
    override val primaryLevel = _primaryLevel.asStateFlow()

    private val _dataConnectionState = MutableStateFlow(DataConnectionState.Disconnected)
    override val dataConnectionState = _dataConnectionState.asStateFlow()

    private val _dataActivityDirection =
        MutableStateFlow(
            DataActivityModel(
                hasActivityIn = false,
                hasActivityOut = false,
            )
        )
    override val dataActivityDirection = _dataActivityDirection.asStateFlow()

    private val _carrierNetworkChangeActive = MutableStateFlow(false)
    override val carrierNetworkChangeActive = _carrierNetworkChangeActive.asStateFlow()

    private val _resolvedNetworkType: MutableStateFlow<ResolvedNetworkType> =
        MutableStateFlow(ResolvedNetworkType.UnknownNetworkType)
    override val resolvedNetworkType = _resolvedNetworkType.asStateFlow()

    override val numberOfLevels = MutableStateFlow(MobileConnectionRepository.DEFAULT_NUM_LEVELS)

    override val dataEnabled = MutableStateFlow(true)

    override val cdmaRoaming = MutableStateFlow(false)

    override val networkName = MutableStateFlow(NetworkNameModel.IntentDerived("demo network"))

    /**
     * Process a new demo mobile event. Note that [resolvedNetworkType] must be passed in separately
     * from the event, due to the requirement to reverse the mobile mappings lookup in the top-level
     * repository.
     */
    fun processDemoMobileEvent(
        event: FakeNetworkEventModel.Mobile,
        resolvedNetworkType: ResolvedNetworkType,
    ) {
        // This is always true here, because we split out disabled states at the data-source level
        dataEnabled.value = true
        networkName.value = NetworkNameModel.IntentDerived(event.name)

        cdmaRoaming.value = event.roaming
        _isRoaming.value = event.roaming
        // TODO(b/261029387): not yet supported
        _isEmergencyOnly.value = false
        _operatorAlphaShort.value = event.name
        _isInService.value = (event.level ?: 0) > 0
        // TODO(b/261029387): not yet supported
        _isGsm.value = false
        _cdmaLevel.value = event.level ?: 0
        _primaryLevel.value = event.level ?: 0
        // TODO(b/261029387): not yet supported
        _dataConnectionState.value = DataConnectionState.Connected
        _dataActivityDirection.value =
            (event.activity ?: TelephonyManager.DATA_ACTIVITY_NONE).toMobileDataActivityModel()
        _carrierNetworkChangeActive.value = event.carrierNetworkChange
        _resolvedNetworkType.value = resolvedNetworkType
    }

    fun processCarrierMergedEvent(event: FakeWifiEventModel.CarrierMerged) {
        // This is always true here, because we split out disabled states at the data-source level
        dataEnabled.value = true
        networkName.value = NetworkNameModel.IntentDerived(CARRIER_MERGED_NAME)
        numberOfLevels.value = event.numberOfLevels
        cdmaRoaming.value = false
        _primaryLevel.value = event.level
        _cdmaLevel.value = event.level
        _dataActivityDirection.value = event.activity.toMobileDataActivityModel()

        // These fields are always the same for carrier-merged networks
        _resolvedNetworkType.value = ResolvedNetworkType.CarrierMergedNetworkType
        _dataConnectionState.value = DataConnectionState.Connected
        _isRoaming.value = false
        _isEmergencyOnly.value = false
        _operatorAlphaShort.value = null
        _isInService.value = true
        _isGsm.value = false
        _carrierNetworkChangeActive.value = false
    }

    companion object {
        private const val CARRIER_MERGED_NAME = "Carrier Merged Network"
    }
}
Loading