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

Commit 6746a755 authored by Caitlin Shkuratov's avatar Caitlin Shkuratov
Browse files

[SB Refactor] Implement demo mode for carrier merged wifi.

Bug: 238425913
Bug: 264684296
Test: `adb shell am broadcast -a com.android.systemui.demo -e command
network -e wifi carriermerged -e level 2` -> displays a mobile triangle
with level 2 and "W+" as its RAT type
Test: See additional test steps in second bug
Test: existing demo mode commands for wifi and mobile still work
Test: atest DemoModeConnectionsRepositoryTest

Change-Id: I9414c0b2566dcc2c49fe78ad352f0cc981ae6404
parent a7790acc
Loading
Loading
Loading
Loading
+107 −20
Original line number Diff line number Diff line
@@ -39,7 +39,11 @@ import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConn
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.toMobileDataActivityModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoModeWifiDataSource
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.model.FakeWifiEventModel
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -60,15 +64,19 @@ import kotlinx.coroutines.launch
class DemoMobileConnectionsRepository
@Inject
constructor(
    private val dataSource: DemoModeMobileConnectionDataSource,
    private val mobileDataSource: DemoModeMobileConnectionDataSource,
    private val wifiDataSource: DemoModeWifiDataSource,
    @Application private val scope: CoroutineScope,
    context: Context,
    private val logFactory: TableLogBufferFactory,
) : MobileConnectionsRepository {

    private var demoCommandJob: Job? = null
    private var mobileDemoCommandJob: Job? = null
    private var wifiDemoCommandJob: Job? = null

    private var connectionRepoCache = mutableMapOf<Int, DemoMobileConnectionRepository>()
    private var carrierMergedSubId: Int? = null

    private var connectionRepoCache = mutableMapOf<Int, CacheContainer>()
    private val subscriptionInfoCache = mutableMapOf<Int, SubscriptionModel>()
    val demoModeFinishedEvent = MutableSharedFlow<Unit>(extraBufferCapacity = 1)

@@ -144,52 +152,83 @@ constructor(
    override val defaultMobileNetworkConnectivity = MutableStateFlow(MobileConnectivityModel())

    override fun getRepoForSubId(subId: Int): DemoMobileConnectionRepository {
        return connectionRepoCache[subId]
            ?: createDemoMobileConnectionRepo(subId).also { connectionRepoCache[subId] = it }
        val current = connectionRepoCache[subId]?.repo
        if (current != null) {
            return current
        }

        val new = createDemoMobileConnectionRepo(subId)
        connectionRepoCache[subId] = new
        return new.repo
    }

    private fun createDemoMobileConnectionRepo(subId: Int): DemoMobileConnectionRepository {
        val tableLogBuffer = logFactory.getOrCreate("DemoMobileConnectionLog [$subId]", 100)
    private fun createDemoMobileConnectionRepo(subId: Int): CacheContainer {
        val tableLogBuffer =
            logFactory.getOrCreate(
                "DemoMobileConnectionLog [$subId]",
                MOBILE_CONNECTION_BUFFER_SIZE,
            )

        return DemoMobileConnectionRepository(
        val repo =
            DemoMobileConnectionRepository(
                subId,
                tableLogBuffer,
            )
        return CacheContainer(repo, lastMobileState = null)
    }

    override val globalMobileDataSettingChangedEvent = MutableStateFlow(Unit)

    fun startProcessingCommands() {
        demoCommandJob =
        mobileDemoCommandJob =
            scope.launch {
                dataSource.mobileEvents.filterNotNull().collect { event -> processEvent(event) }
                mobileDataSource.mobileEvents.filterNotNull().collect { event ->
                    processMobileEvent(event)
                }
            }
        wifiDemoCommandJob =
            scope.launch {
                wifiDataSource.wifiEvents.filterNotNull().collect { event ->
                    processWifiEvent(event)
                }
            }
    }

    fun stopProcessingCommands() {
        demoCommandJob?.cancel()
        mobileDemoCommandJob?.cancel()
        wifiDemoCommandJob?.cancel()
        _subscriptions.value = listOf()
        connectionRepoCache.clear()
        subscriptionInfoCache.clear()
    }

    private fun processEvent(event: FakeNetworkEventModel) {
    private fun processMobileEvent(event: FakeNetworkEventModel) {
        when (event) {
            is Mobile -> {
                processEnabledMobileState(event)
            }
            is MobileDisabled -> {
                processDisabledMobileState(event)
                maybeRemoveSubscription(event.subId)
            }
        }
    }

    private fun processWifiEvent(event: FakeWifiEventModel) {
        when (event) {
            is FakeWifiEventModel.WifiDisabled -> disableCarrierMerged()
            is FakeWifiEventModel.Wifi -> disableCarrierMerged()
            is FakeWifiEventModel.CarrierMerged -> processCarrierMergedWifiState(event)
        }
    }

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

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

        // 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)
@@ -198,14 +237,36 @@ constructor(
        connection.connectionInfo.value = state.toMobileConnectionModel()
    }

    private fun processDisabledMobileState(state: MobileDisabled) {
    private fun processCarrierMergedWifiState(event: FakeWifiEventModel.CarrierMerged) {
        // The new carrier merged connection is for a different sub ID, so disable carrier merged
        // for the current (now old) sub
        if (carrierMergedSubId != event.subscriptionId) {
            disableCarrierMerged()
        }

        // get or create the connection repo, and set its values
        val subId = event.subscriptionId
        maybeCreateSubscription(subId)
        carrierMergedSubId = 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.Derived(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}")
    }

    private fun maybeRemoveSubscription(subId: Int?) {
        if (_subscriptions.value.isEmpty()) {
            // Nothing to do here
            return
        }

        val subId =
            state.subId
        val finalSubId =
            subId
                ?: run {
                    // For sake of usability, we can allow for no subId arg if there is only one
                    // subscription
@@ -223,7 +284,21 @@ constructor(
                    _subscriptions.value[0].subscriptionId
                }

        removeSubscription(subId)
        removeSubscription(finalSubId)
    }

    private fun disableCarrierMerged() {
        val currentCarrierMergedSubId = carrierMergedSubId ?: return

        // If this sub ID was previously not carrier merged, we should reset it to its previous
        // connection.
        val lastMobileState = connectionRepoCache[carrierMergedSubId]?.lastMobileState
        if (lastMobileState != null) {
            processEnabledMobileState(lastMobileState)
        } else {
            // Otherwise, just remove the subscription entirely
            removeSubscription(currentCarrierMergedSubId)
        }
    }

    private fun removeSubscription(subId: Int) {
@@ -251,6 +326,10 @@ constructor(
        )
    }

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

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

        private const val DEFAULT_SUB_ID = 1

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

class CacheContainer(
    var repo: DemoMobileConnectionRepository,
    /** The last received [Mobile] event. Used when switching from carrier merged back to mobile. */
    var lastMobileState: Mobile?,
)

class DemoMobileConnectionRepository(
    override val subId: Int,
    override val tableLogBuffer: TableLogBuffer,
+17 −4
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.demomode.DemoMode.COMMAND_NETWORK
import com.android.systemui.demomode.DemoModeController
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.model.FakeWifiEventModel
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -43,10 +44,10 @@ constructor(

    private fun Bundle.toWifiEvent(): FakeWifiEventModel? {
        val wifi = getString("wifi") ?: return null
        return if (wifi == "show") {
            activeWifiEvent()
        } else {
            FakeWifiEventModel.WifiDisabled
        return when (wifi) {
            "show" -> activeWifiEvent()
            "carriermerged" -> carrierMergedWifiEvent()
            else -> FakeWifiEventModel.WifiDisabled
        }
    }

@@ -64,6 +65,14 @@ constructor(
        )
    }

    private fun Bundle.carrierMergedWifiEvent(): FakeWifiEventModel.CarrierMerged {
        val subId = getString("slot")?.toInt() ?: DEFAULT_CARRIER_MERGED_SUB_ID
        val level = getString("level")?.toInt() ?: 0
        val numberOfLevels = getString("numlevels")?.toInt() ?: DEFAULT_NUM_LEVELS

        return FakeWifiEventModel.CarrierMerged(subId, level, numberOfLevels)
    }

    private fun String.toActivity(): Int =
        when (this) {
            "inout" -> WifiManager.TrafficStateCallback.DATA_ACTIVITY_INOUT
@@ -71,4 +80,8 @@ constructor(
            "out" -> WifiManager.TrafficStateCallback.DATA_ACTIVITY_OUT
            else -> WifiManager.TrafficStateCallback.DATA_ACTIVITY_NONE
        }

    companion object {
        const val DEFAULT_CARRIER_MERGED_SUB_ID = 10
    }
}
+16 −1
Original line number Diff line number Diff line
@@ -66,6 +66,7 @@ constructor(
    private fun processEvent(event: FakeWifiEventModel) =
        when (event) {
            is FakeWifiEventModel.Wifi -> processEnabledWifiState(event)
            is FakeWifiEventModel.CarrierMerged -> processCarrierMergedWifiState(event)
            is FakeWifiEventModel.WifiDisabled -> processDisabledWifiState()
        }

@@ -85,6 +86,14 @@ constructor(
        _wifiNetwork.value = event.toWifiNetworkModel()
    }

    private fun processCarrierMergedWifiState(event: FakeWifiEventModel.CarrierMerged) {
        _isWifiEnabled.value = true
        _isWifiDefault.value = true
        // TODO(b/238425913): Support activity in demo mode.
        _wifiActivity.value = DataActivityModel(hasActivityIn = false, hasActivityOut = false)
        _wifiNetwork.value = event.toCarrierMergedModel()
    }

    private fun FakeWifiEventModel.Wifi.toWifiNetworkModel(): WifiNetworkModel =
        WifiNetworkModel.Active(
            networkId = DEMO_NET_ID,
@@ -99,7 +108,13 @@ constructor(
            passpointProviderFriendlyName = null,
        )

    // TODO(b/238425913): Add carrier merged support.
    private fun FakeWifiEventModel.CarrierMerged.toCarrierMergedModel(): WifiNetworkModel =
        WifiNetworkModel.CarrierMerged(
            networkId = DEMO_NET_ID,
            subscriptionId = subscriptionId,
            level = level,
            numberOfLevels = numberOfLevels,
        )

    companion object {
        private const val DEMO_NET_ID = 1234
+6 −0
Original line number Diff line number Diff line
@@ -29,5 +29,11 @@ sealed interface FakeWifiEventModel {
        val validated: Boolean?,
    ) : FakeWifiEventModel

    data class CarrierMerged(
        val subscriptionId: Int,
        val level: Int,
        val numberOfLevels: Int,
    ) : FakeWifiEventModel

    object WifiDisabled : FakeWifiEventModel
}
+10 −3
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.Mobil
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoModeWifiDataSource
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.kotlinArgumentCaptor
import com.android.systemui.util.mockito.mock
@@ -72,7 +73,8 @@ class MobileRepositorySwitcherTest : SysuiTestCase() {
    private lateinit var underTest: MobileRepositorySwitcher
    private lateinit var realRepo: MobileConnectionsRepositoryImpl
    private lateinit var demoRepo: DemoMobileConnectionsRepository
    private lateinit var mockDataSource: DemoModeMobileConnectionDataSource
    private lateinit var mobileDataSource: DemoModeMobileConnectionDataSource
    private lateinit var wifiDataSource: DemoModeWifiDataSource
    private lateinit var logFactory: TableLogBufferFactory
    private lateinit var wifiRepository: FakeWifiRepository

@@ -98,10 +100,14 @@ class MobileRepositorySwitcherTest : SysuiTestCase() {
        // Never start in demo mode
        whenever(demoModeController.isInDemoMode).thenReturn(false)

        mockDataSource =
        mobileDataSource =
            mock<DemoModeMobileConnectionDataSource>().also {
                whenever(it.mobileEvents).thenReturn(fakeNetworkEventsFlow)
            }
        wifiDataSource =
            mock<DemoModeWifiDataSource>().also {
                whenever(it.wifiEvents).thenReturn(MutableStateFlow(null))
            }
        wifiRepository = FakeWifiRepository()

        realRepo =
@@ -122,7 +128,8 @@ class MobileRepositorySwitcherTest : SysuiTestCase() {

        demoRepo =
            DemoMobileConnectionsRepository(
                dataSource = mockDataSource,
                mobileDataSource = mobileDataSource,
                wifiDataSource = wifiDataSource,
                scope = scope,
                context = context,
                logFactory = logFactory,
Loading