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

Commit f5846cc3 authored by Caitlin Shkuratov's avatar Caitlin Shkuratov Committed by Android (Google) Code Review
Browse files

Merge changes I84922156,Icb06bf4d into main

* changes:
  [SB] Don't use conflatedCallbackFlow for BatteryRepository.
  Update test TableLogBuffers to use LogProxyDefault.
parents 610bc9da 31b51667
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -501,6 +501,16 @@ flag {
    }
}

flag {
    name: "status_bar_battery_no_conflation"
    namespace: "systemui"
    description: "Use callbackFlow instead of conflatedCallbackFlow in BatteryRepository"
    bug: "433239990"
    metadata {
      purpose: PURPOSE_BUGFIX
    }
}

flag {
    name: "icon_refresh_2025"
    namespace: "systemui"
+208 −1
Original line number Diff line number Diff line
@@ -17,9 +17,11 @@
package com.android.systemui.statusbar.pipeline.battery.data.repository

import android.content.testableContext
import android.platform.test.annotations.EnableFlags
import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.backgroundScope
@@ -27,6 +29,7 @@ import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useStandardTestDispatcher
import com.android.systemui.log.table.logcatTableLogBuffer
import com.android.systemui.shared.settings.data.repository.fakeSystemSettingsRepository
import com.android.systemui.shared.settings.data.repository.systemSettingsRepository
@@ -37,6 +40,7 @@ import com.google.common.truth.Truth.assertThat
import kotlin.time.Duration.Companion.minutes
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runCurrent
import org.junit.Test
import org.junit.runner.RunWith

@@ -44,7 +48,8 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class BatteryRepositoryTest : SysuiTestCase() {
    val kosmos = testKosmos()
    // Always use standard dispatcher so that our backpressure tests work correctly.
    val kosmos = testKosmos().useStandardTestDispatcher()

    val Kosmos.underTest by
        Kosmos.Fixture {
@@ -183,4 +188,206 @@ class BatteryRepositoryTest : SysuiTestCase() {

            assertThat(latest).isFalse()
        }

    /** Regression test for b/433239990. */
    @Test
    @EnableFlags(Flags.FLAG_STATUS_BAR_BATTERY_NO_CONFLATION)
    fun backpressure_isStateUnknown() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.isStateUnknown)
            testScope.runCurrent()
            assertThat(latest).isFalse()

            batteryController.fake._isStateUnknown = true
            testScope.runCurrent()
            assertThat(latest).isTrue()

            // Introduce backpressure by sending multiple events before `runCurrent`
            // The first and the last event will be processed, but the middle event would get
            // dropped if there's backpressure. So, put the event we care about in the middle.
            batteryController.fake._isPowerSave = true
            batteryController.fake._isStateUnknown = false
            batteryController.fake._isDefender = true

            // Use many `runCurrent`s to be sure we've processed all the events we have
            testScope.runCurrent()
            testScope.runCurrent()
            testScope.runCurrent()
            testScope.runCurrent()

            // Verify we didn't drop the middle unknown=false event
            assertThat(latest).isFalse()
        }

    @Test
    @EnableFlags(Flags.FLAG_STATUS_BAR_BATTERY_NO_CONFLATION)
    fun backpressure_isPluggedIn() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.isPluggedIn)
            testScope.runCurrent()
            assertThat(latest).isFalse()

            batteryController.fake._isPluggedIn = true
            testScope.runCurrent()
            assertThat(latest).isTrue()

            // Introduce backpressure by sending multiple events before `runCurrent`
            // The first and the last event will be processed, but the middle event would get
            // dropped if there's backpressure. So, put the event we care about in the middle.
            batteryController.fake._isPowerSave = true
            batteryController.fake._isPluggedIn = false
            batteryController.fake._isDefender = true

            // Use many `runCurrent`s to be sure we've processed all the events we have
            testScope.runCurrent()
            testScope.runCurrent()
            testScope.runCurrent()
            testScope.runCurrent()

            // Verify we didn't drop the middle isPluggedIn=false event
            assertThat(latest).isFalse()
        }

    @Test
    @EnableFlags(Flags.FLAG_STATUS_BAR_BATTERY_NO_CONFLATION)
    fun backpressure_isPowerSave() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.isPowerSaveEnabled)
            testScope.runCurrent()
            assertThat(latest).isFalse()

            batteryController.fake._isPowerSave = true
            testScope.runCurrent()
            assertThat(latest).isTrue()

            // Introduce backpressure by sending multiple events before `runCurrent`
            // The first and the last event will be processed, but the middle event would get
            // dropped if there's backpressure. So, put the event we care about in the middle.
            batteryController.fake._level = 44
            batteryController.fake._isPowerSave = false
            batteryController.fake._isPluggedIn = true

            // Use many `runCurrent`s to be sure we've processed all the events we have
            testScope.runCurrent()
            testScope.runCurrent()
            testScope.runCurrent()
            testScope.runCurrent()

            // Verify we didn't drop the middle isPowerSave=false event
            assertThat(latest).isFalse()
        }

    @Test
    @EnableFlags(Flags.FLAG_STATUS_BAR_BATTERY_NO_CONFLATION)
    fun backpressure_isExtremePowerSave() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.isExtremePowerSaveEnabled)
            testScope.runCurrent()
            assertThat(latest).isFalse()

            batteryController.fake._isExtremePowerSave = true
            testScope.runCurrent()
            assertThat(latest).isTrue()

            // Introduce backpressure by sending multiple events before `runCurrent`
            // The first and the last event will be processed, but the middle event would get
            // dropped if there's backpressure. So, put the event we care about in the middle.
            batteryController.fake._isPluggedIn = true
            batteryController.fake._isExtremePowerSave = false
            batteryController.fake._level = 33

            // Use many `runCurrent`s to be sure we've processed all the events we have
            testScope.runCurrent()
            testScope.runCurrent()
            testScope.runCurrent()
            testScope.runCurrent()

            // Verify we didn't drop the middle isExtremePowerSave=false event
            assertThat(latest).isFalse()
        }

    @Test
    @EnableFlags(Flags.FLAG_STATUS_BAR_BATTERY_NO_CONFLATION)
    fun backpressure_isBatteryDefender() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.isBatteryDefenderEnabled)
            testScope.runCurrent()
            assertThat(latest).isFalse()

            batteryController.fake._isDefender = true
            testScope.runCurrent()
            assertThat(latest).isTrue()

            // Introduce backpressure by sending multiple events before `runCurrent`
            // The first and the last event will be processed, but the middle event would get
            // dropped if there's backpressure. So, put the event we care about in the middle.
            batteryController.fake._isPluggedIn = true
            batteryController.fake._isDefender = false
            batteryController.fake._isPowerSave = true

            // Use many `runCurrent`s to be sure we've processed all the events we have
            testScope.runCurrent()
            testScope.runCurrent()
            testScope.runCurrent()
            testScope.runCurrent()

            // Verify we didn't drop the middle isDefender=false event
            assertThat(latest).isFalse()
        }

    @Test
    @EnableFlags(Flags.FLAG_STATUS_BAR_BATTERY_NO_CONFLATION)
    fun backpressure_level() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.level)
            testScope.runCurrent()
            batteryController.fake._level = 10
            testScope.runCurrent()
            assertThat(latest).isEqualTo(10)

            // Introduce backpressure by sending multiple events before `runCurrent`
            // The first and the last event will be processed, but the middle event would get
            // dropped if there's backpressure. So, put the event we care about in the middle.
            batteryController.fake._isPluggedIn = true
            batteryController.fake._level = 15
            batteryController.fake._isPowerSave = true

            // Use many `runCurrent`s to be sure we've processed all the events we have
            testScope.runCurrent()
            testScope.runCurrent()
            testScope.runCurrent()
            testScope.runCurrent()

            // Verify we didn't drop the middle level=15 event
            assertThat(latest).isEqualTo(15)
        }

    @Test
    @EnableFlags(Flags.FLAG_STATUS_BAR_BATTERY_NO_CONFLATION)
    fun backpressure_isIncompatibleCharging() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.isIncompatibleCharging)
            testScope.runCurrent()
            assertThat(latest).isFalse()

            batteryController.fake._isIncompatibleCharging = true
            testScope.runCurrent()
            assertThat(latest).isTrue()

            // Introduce backpressure by sending multiple events before `runCurrent`
            // The first and the last event will be processed, but the middle event would get
            // dropped if there's backpressure. So, put the event we care about in the middle.
            batteryController.fake._isPluggedIn = true
            batteryController.fake._isIncompatibleCharging = false
            batteryController.fake._isPowerSave = true

            // Use many `runCurrent`s to be sure we've processed all the events we have
            testScope.runCurrent()
            testScope.runCurrent()
            testScope.runCurrent()
            testScope.runCurrent()

            // Verify we didn't drop the middle isIncompatibleCharging=false event
            assertThat(latest).isFalse()
        }
}
+14 −1
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.pipeline.battery.data.repository

import android.content.Context
import android.provider.Settings
import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
@@ -32,11 +33,13 @@ import kotlin.coroutines.resume
import kotlin.time.Duration.Companion.minutes
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.ProducerScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
@@ -99,8 +102,18 @@ constructor(
    settingsRepository: SystemSettingsRepository,
    @BatteryTableLog tableLog: TableLogBuffer,
) : BatteryRepository {
    private fun <T> flaggedCallbackFlow(block: suspend ProducerScope<T>.() -> Unit): Flow<T> {
        if (Flags.statusBarBatteryNoConflation()) {
            return callbackFlow(block)
        } else {
            return conflatedCallbackFlow(block)
        }
    }

    private val batteryState: StateFlow<BatteryCallbackState> =
        conflatedCallbackFlow<(BatteryCallbackState) -> BatteryCallbackState> {
        // Never use conflatedCallbackFlow here because that could cause us to drop events.
        // See b/433239990.
        flaggedCallbackFlow<(BatteryCallbackState) -> BatteryCallbackState> {
                val callback =
                    object : BatteryController.BatteryStateChangeCallback {
                        override fun onBatteryLevelChanged(
+2 −2
Original line number Diff line number Diff line
@@ -18,8 +18,8 @@ package com.android.systemui.log.table

import com.android.systemui.dump.dumpManager
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.log.FakeLogProxy
import com.android.systemui.log.LogcatEchoTrackerAlways
import com.android.systemui.log.impl.LogProxyDefault
import com.android.systemui.log.table.impl.TableLogBufferFactoryImpl
import com.android.systemui.util.time.fakeSystemClock

@@ -29,6 +29,6 @@ val Kosmos.tableLogBufferFactory: TableLogBufferFactory by
            dumpManager = dumpManager,
            systemClock = fakeSystemClock,
            logcatEchoTracker = LogcatEchoTrackerAlways(),
            logProxy = FakeLogProxy(),
            logProxy = LogProxyDefault(),
        )
    }
+2 −2
Original line number Diff line number Diff line
@@ -17,8 +17,8 @@
package com.android.systemui.log.table

import com.android.systemui.kosmos.Kosmos
import com.android.systemui.log.FakeLogProxy
import com.android.systemui.log.LogcatEchoTrackerAlways
import com.android.systemui.log.impl.LogProxyDefault
import com.android.systemui.util.time.SystemClock
import com.android.systemui.util.time.fakeSystemClock

@@ -39,5 +39,5 @@ fun logcatTableLogBuffer(systemClock: SystemClock, name: String = "EchoToLogcatT
        name,
        systemClock,
        logcatEchoTracker = LogcatEchoTrackerAlways(),
        localLogcat = FakeLogProxy(),
        localLogcat = LogProxyDefault(),
    )