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

Commit 847a2d81 authored by Steve Elliott's avatar Steve Elliott
Browse files

[kairos] ensure demo wifi events aren't missed

Unlike the Flow implementation, Kairos' demo mode stack isn't stood up
until demo mode is enabled. This means that if a command both enables
demo mode *and* sets some demo mode state, the Kairos implementation
might miss the state update if it was emitted from a Flow without a
replay cache.

The fix, other than to rewrite the DemoModeWifiDataSource entirely in
Kairos, is to simply keep the Flow alive by ensuring that the downstream
Events is always observed. This ensures that the data source's events
are seen by Kairos and can be picked up in the same transaction that
demo mode is enabled.

Bug: 425675342
Test: manual
  1. adb shell am broadcast -a com.android.systemui.demo -e command network -e wifi carriermerged -e slot 18 -e level 4 -e numlevels 5 -e inflate false
  Observe: W+ icon shows w/ 4 out of 4 signal bars
Flag: com.android.systemui.status_bar_mobile_icon_kairos
Change-Id: If5e333058af4c69b29252e0be7b57e3d4bdc8f4b
parent 9beea219
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -72,6 +72,7 @@ import com.android.systemui.statusbar.pipeline.shared.data.repository.Connectivi
import com.android.systemui.statusbar.pipeline.wifi.data.repository.RealWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositorySwitcher
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoModeWifiDataSourceKairos
import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.DisabledWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
@@ -94,6 +95,7 @@ import kotlinx.coroutines.flow.Flow
        [
            AirplaneModeDataLayerModule::class,
            DemoModeMobileConnectionDataSourceKairosImpl.Module::class,
            DemoModeWifiDataSourceKairos.Module::class,
            MobileRepositorySwitcherKairos.Module::class,
            MobileConnectionsRepositoryKairosImpl.Module::class,
            MobileIconsInteractorKairosImpl.Module::class,
+4 −7
Original line number Diff line number Diff line
@@ -55,7 +55,7 @@ import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model
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.FullMobileConnectionRepository.Factory.Companion.MOBILE_CONNECTION_BUFFER_SIZE
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoModeWifiDataSource
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoModeWifiDataSourceKairos
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.model.FakeWifiEventModel
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@@ -66,7 +66,7 @@ class DemoMobileConnectionsRepositoryKairos
@AssistedInject
constructor(
    mobileDataSource: DemoModeMobileConnectionDataSourceKairos,
    private val wifiDataSource: DemoModeWifiDataSource,
    private val wifiDataSource: DemoModeWifiDataSourceKairos,
    context: Context,
    private val logFactory: TableLogBufferFactory,
) : MobileConnectionsRepositoryKairos, KairosBuilder by kairosBuilder() {
@@ -76,11 +76,8 @@ constructor(
        fun create(): DemoMobileConnectionsRepositoryKairos
    }

    private val wifiEvents: Events<FakeWifiEventModel?> = buildEvents {
        wifiDataSource.wifiEvents.toEvents(
            nameTag("DemoMobileConnectionsRepositoryKairos.wifiEvents")
        )
    }
    private val wifiEvents: Events<FakeWifiEventModel?>
        get() = wifiDataSource.wifiEvents

    private val mobileEventsWithSubId: Events<Pair<Int, FakeNetworkEventModel>> =
        mobileDataSource.mobileEvents.mapNotNull { event ->
+69 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.wifi.data.repository.demo

import com.android.systemui.KairosActivatable
import com.android.systemui.KairosBuilder
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.kairos.Events
import com.android.systemui.kairos.ExperimentalKairosApi
import com.android.systemui.kairos.util.nameTag
import com.android.systemui.kairosBuilder
import com.android.systemui.statusbar.pipeline.mobile.StatusBarMobileIconKairos
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.model.FakeWifiEventModel
import dagger.Provides
import dagger.multibindings.ElementsIntoSet
import javax.inject.Inject
import javax.inject.Provider

/**
 * A wrapper around [DemoModeWifiDataSource] that re-exposes Flows as Kairos [Events].
 *
 * @see DemoModeWifiDataSource
 */
@ExperimentalKairosApi
@SysUISingleton
class DemoModeWifiDataSourceKairos
private constructor(
    private val unwrapped: DemoModeWifiDataSource,
    private val kairosBuilder: KairosBuilder,
) : KairosActivatable by kairosBuilder {

    @Inject
    constructor(
        demoModeWifiDataSource: DemoModeWifiDataSource
    ) : this(demoModeWifiDataSource, kairosBuilder())

    val wifiEvents: Events<FakeWifiEventModel?> =
        kairosBuilder.buildEvents {
            unwrapped.wifiEvents
                .toEvents(nameTag("DemoModeWifiDataSourceKairos.wifiEvents"))
                // Ensure that we are always observing; this keeps the underlying SharedFlow alive
                // and ensures that events aren't missed the instant demo mode is activated.
                .apply { observeSync() }
        }

    @dagger.Module
    object Module {
        @Provides
        @ElementsIntoSet
        fun kairosActivatable(
            impl: Provider<DemoModeWifiDataSourceKairos>
        ): Set<@JvmSuppressWildcards KairosActivatable> =
            if (StatusBarMobileIconKairos.isEnabled) setOf(impl.get()) else emptySet()
    }
}
+10 −1
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ import com.android.systemui.statusbar.pipeline.mobile.util.FakeSubscriptionManag
import com.android.systemui.statusbar.pipeline.mobile.util.SubscriptionManagerProxy
import com.android.systemui.statusbar.pipeline.shared.data.repository.connectivityRepository
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoModeWifiDataSource
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoModeWifiDataSourceKairos
import com.android.systemui.statusbar.pipeline.wifi.data.repository.wifiRepository
import com.android.systemui.util.mockito.mockFixture
import org.mockito.kotlin.mock
@@ -63,7 +64,7 @@ val Kosmos.fakeMobileConnectionsRepositoryKairos by ActivatedKairosFixture {
val Kosmos.demoMobileConnectionsRepositoryKairos by ActivatedKairosFixture {
    DemoMobileConnectionsRepositoryKairos(
        mobileDataSource = demoModeMobileConnectionDataSourceKairos,
        wifiDataSource = wifiDataSource,
        wifiDataSource = wifiDataSourceKairos,
        context = applicationContext,
        logFactory = tableLogBufferFactory,
    )
@@ -77,6 +78,11 @@ val Kosmos.demoModeMobileConnectionDataSourceKairos:

val Kosmos.wifiDataSource: DemoModeWifiDataSource by mockFixture()

@ExperimentalKairosApi
val Kosmos.wifiDataSourceKairos: DemoModeWifiDataSourceKairos by ActivatedKairosFixture {
    DemoModeWifiDataSourceKairos(wifiDataSource)
}

@ExperimentalKairosApi
class FakeDemoModeMobileConnectionDataSourceKairos(kairos: KairosNetwork) :
    DemoModeMobileConnectionDataSourceKairos {
@@ -100,6 +106,9 @@ val Kosmos.mobileRepositorySwitcherKairos:
@ExperimentalKairosApi
val Kosmos.demoMobileConnectionsRepositoryKairosFactory:
    DemoMobileConnectionsRepositoryKairos.Factory by Fixture {
    // query the wifiDataSourceKairos fixture here, so that it is ready to go when the factory is
    // invoked
    val wifiDataSource = wifiDataSourceKairos
    DemoMobileConnectionsRepositoryKairos.Factory {
        DemoMobileConnectionsRepositoryKairos(
            mobileDataSource = demoModeMobileConnectionDataSourceKairos,