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

Commit ea30bf35 authored by Michal Brzezinski's avatar Michal Brzezinski Committed by Michał Brzeziński
Browse files

Not measuring latency when fold event results in screen off

- changing toState: now screen off or state AOD means that it just ended up in the state, not necessarily because of folding
- if final state is screen off we don’t care about latency - which fixes the problem of very small latencies coming from case when we’re folding while screen is off
- filling out “fromState” field in DisplaySwitchLatencyEvent
- updating javadoc to clarify that Switching doesn’t necessarily mean we’re turning on/off any display
- small fixes in tests - removing not needed setScreenPowerState and simplifying some tests

Bug: 394257187
Flag: NONE multiple small changes
Test: DisplaySwitchLatencyTrackerTest
Change-Id: Id5b92d23d9f2b41b24a8a69851e4dc0d86f7838e
parent 9a8e23f8
Loading
Loading
Loading
Loading
+47 −35
Original line number Diff line number Diff line
@@ -31,17 +31,13 @@ import com.android.systemui.deviceStateManager
import com.android.systemui.display.data.repository.DeviceStateRepository.DeviceState.FOLDED
import com.android.systemui.display.data.repository.DeviceStateRepository.DeviceState.HALF_FOLDED
import com.android.systemui.display.data.repository.DeviceStateRepository.DeviceState.UNFOLDED
import com.android.systemui.display.data.repository.fakeDeviceStateRepository
import com.android.systemui.foldedDeviceStateList
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setScreenPowerState
import com.android.systemui.power.domain.interactor.PowerInteractorFactory
import com.android.systemui.power.shared.model.ScreenPowerState.SCREEN_OFF
import com.android.systemui.power.shared.model.ScreenPowerState.SCREEN_ON
import com.android.systemui.shared.system.SysUiStatsLog
import com.android.systemui.testKosmos
import com.android.systemui.unfold.DisplaySwitchLatencyTracker.Companion.FOLDABLE_DEVICE_STATE_CLOSED
@@ -82,7 +78,6 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
    private val testScope: TestScope = kosmos.testScope

    private val resources = mock<Resources>()
    private val deviceStateRepository = kosmos.fakeDeviceStateRepository
    private val powerInteractor = PowerInteractorFactory.create().powerInteractor
    private val keyguardInteractor = mock<KeyguardInteractor>()
    private val displaySwitchLatencyLogger = mock<DisplaySwitchLatencyLogger>()
@@ -106,7 +101,6 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
        whenever(keyguardInteractor.isAodAvailable).thenReturn(isAodAvailable)
        whenever(screenTimeoutPolicyRepository.screenTimeoutActive).thenReturn(screenTimeoutActive)
        powerInteractor.setAwakeForTest()
        powerInteractor.setScreenPowerState(SCREEN_ON)

        setDisplaySwitchState(Idle(newDeviceState = FOLDED))

@@ -166,7 +160,6 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
            setDisplaySwitchState(Idle(HALF_FOLDED))

            setDisplaySwitchState(Switching(HALF_FOLDED))
            powerInteractor.setScreenPowerState(SCREEN_ON)
            systemClock.advanceTime(200)
            setDisplaySwitchState(Idle(FOLDED))

@@ -185,22 +178,13 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
        testScope.runTest {
            isAodAvailable.emit(true)
            setDisplaySwitchState(Idle(HALF_FOLDED))
            setDisplaySwitchState(Switching(HALF_FOLDED))

            setDisplaySwitchState(Switching(HALF_FOLDED))
            powerInteractor.setAsleepForTest(sleepReason = GO_TO_SLEEP_REASON_DEVICE_FOLD)
            powerInteractor.setScreenPowerState(SCREEN_OFF)

            systemClock.advanceTime(200)
            setDisplaySwitchState(Idle(FOLDED))

            val expectedLoggedEvent =
                successfulEvent(
                    latencyMs = 200,
                    fromFoldableDeviceState = FOLDABLE_DEVICE_STATE_HALF_OPEN,
                    toFoldableDeviceState = FOLDABLE_DEVICE_STATE_CLOSED,
                    toState = SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TO_STATE__AOD,
                )
            assertThat(capturedLogEvent()).isEqualTo(expectedLoggedEvent)
            assertThat(capturedLogEvent().toState)
                .isEqualTo(SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TO_STATE__AOD)
        }
    }

@@ -211,20 +195,54 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
            isAodAvailable.emit(false)

            setDisplaySwitchState(Switching(HALF_FOLDED))

            powerInteractor.setAsleepForTest(sleepReason = GO_TO_SLEEP_REASON_DEVICE_FOLD)
            powerInteractor.setScreenPowerState(SCREEN_OFF)
            setDisplaySwitchState(Idle(FOLDED))

            assertThat(capturedLogEvent().toState)
                .isEqualTo(SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TO_STATE__SCREEN_OFF)
        }
    }

    @Test
    fun foldingWhileScreenIsAlreadyOff_capturesToStateAsScreenOff() {
        testScope.runTest {
            setDisplaySwitchState(Idle(HALF_FOLDED))
            powerInteractor.setAsleepForTest()

            setDisplaySwitchState(Switching(HALF_FOLDED))
            setDisplaySwitchState(Idle(FOLDED))

            val expectedLoggedEvent =
                successfulEvent(
                    latencyMs = 0,
                    fromFoldableDeviceState = FOLDABLE_DEVICE_STATE_HALF_OPEN,
                    toFoldableDeviceState = FOLDABLE_DEVICE_STATE_CLOSED,
                    toState = SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TO_STATE__SCREEN_OFF,
                )
            assertThat(capturedLogEvent()).isEqualTo(expectedLoggedEvent)
            assertThat(capturedLogEvent().toState)
                .isEqualTo(SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TO_STATE__SCREEN_OFF)
        }
    }

    @Test
    fun foldingWhileScreenIsAlreadyOff_capturesFromStateAsScreenOff() {
        testScope.runTest {
            setDisplaySwitchState(Idle(HALF_FOLDED))
            powerInteractor.setAsleepForTest()

            setDisplaySwitchState(Switching(HALF_FOLDED))
            setDisplaySwitchState(Idle(FOLDED))

            assertThat(capturedLogEvent().fromState)
                .isEqualTo(SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__FROM_STATE__SCREEN_OFF)
        }
    }

    @Test
    fun foldingWhileAod_capturesFromStateAsAod() {
        testScope.runTest {
            setDisplaySwitchState(Idle(HALF_FOLDED))
            powerInteractor.setAsleepForTest()
            isAodAvailable.value = true

            setDisplaySwitchState(Switching(HALF_FOLDED))
            setDisplaySwitchState(Idle(FOLDED))

            assertThat(capturedLogEvent().fromState)
                .isEqualTo(SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__FROM_STATE__AOD)
        }
    }

@@ -237,7 +255,6 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
            setDisplaySwitchState(Switching(HALF_FOLDED))

            powerInteractor.setAsleepForTest(sleepReason = GO_TO_SLEEP_REASON_DEVICE_FOLD)
            powerInteractor.setScreenPowerState(SCREEN_OFF)

            setDisplaySwitchState(Idle(FOLDED))

@@ -303,7 +320,6 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
            setDisplaySwitchState(Idle(HALF_FOLDED))

            setDisplaySwitchState(Switching(FOLDED))
            powerInteractor.setScreenPowerState(SCREEN_ON)
            setDisplaySwitchState(Idle(FOLDED))

            verify(latencyTracker).onActionEnd(ACTION_SWITCH_DISPLAY_FOLD)
@@ -349,7 +365,6 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
            setDisplaySwitchState(Switching(HALF_FOLDED))

            setDisplaySwitchState(Corrupted(HALF_FOLDED))
            powerInteractor.setScreenPowerState(SCREEN_ON)
            setDisplaySwitchState(Idle(UNFOLDED))

            verify(latencyTracker).onActionCancel(ACTION_SWITCH_DISPLAY_FOLD)
@@ -363,7 +378,6 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
            setDisplaySwitchState(Switching(HALF_FOLDED))
            setDisplaySwitchState(Corrupted(HALF_FOLDED))
            setDisplaySwitchState(Idle(FOLDED))
            powerInteractor.setScreenPowerState(SCREEN_ON)

            setDisplaySwitchState(Switching(HALF_FOLDED))
            setDisplaySwitchState(Idle(UNFOLDED))
@@ -381,7 +395,6 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
            setDisplaySwitchState(Idle(UNFOLDED))

            setDisplaySwitchState(Switching(FOLDED))
            powerInteractor.setScreenPowerState(SCREEN_ON)
            setDisplaySwitchState(Idle(FOLDED))

            verify(latencyTracker, times(2)).onActionStart(ACTION_SWITCH_DISPLAY_FOLD)
@@ -420,7 +433,6 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
    fun displaySwitchTimedOut_foldTrackingCancelled() {
        testScope.runTest {
            setDisplaySwitchState(Switching(HALF_FOLDED))
            powerInteractor.setScreenPowerState(SCREEN_ON)

            setDisplaySwitchState(Idle(FOLDED, timedOut = true))

+24 −13
Original line number Diff line number Diff line
@@ -28,7 +28,6 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.display.data.repository.DeviceStateRepository.DeviceState
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.shared.system.SysUiStatsLog
import com.android.systemui.unfold.DisplaySwitchLatencyTracker.DisplaySwitchLatencyEvent
import com.android.systemui.unfold.DisplaySwitchLatencyTracker.TrackingResult.CORRUPTED
@@ -103,7 +102,7 @@ constructor(
                    } else {
                        latencyTracker.onActionEnd(ACTION_SWITCH_DISPLAY_UNFOLD)

                        if (getCurrentState()
                        if (getToState()
                            != SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TO_STATE__SCREEN_OFF
                        ) {
                            latencyTracker.onActionEnd(ACTION_SWITCH_DISPLAY_FOLD)
@@ -150,8 +149,13 @@ constructor(
                else -> SUCCESS
            }
        val event = switchingUpdate.event ?: return
        val displaySwitchTimeMs = updatesChain.finalUpdate.elapsedTime - switchingUpdate.elapsedTime
        val toState = getCurrentState()
        val toState = getToState()
        val displaySwitchTimeMs =
            if (isStateScreenOff()) {
                LATENCY_UNDEFINED // we don't care about latency in this case
            } else {
                updatesChain.finalUpdate.elapsedTime - switchingUpdate.elapsedTime
            }
        log {
            "trackingResult=$trackingResult, " +
                "fromFoldableDeviceState=${startingIdleState.switchState.newDeviceState}" +
@@ -232,23 +236,29 @@ constructor(
            TIMED_OUT -> SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TRACKING_RESULT__TIMED_OUT
        }

    private fun getCurrentState(): Int =
    private fun getToState(): Int =
        // not checking asleep/screen off reason means we misrepresent toState for case when user
        // folds and quickly puts device to sleep with power button. But still it seems better
        // than not putting SCREEN_OFF as reason when device is just asleep and user folds.
        when {
            isStateAod() -> SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TO_STATE__AOD
            isStateScreenOff() -> SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TO_STATE__SCREEN_OFF
            else -> SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TO_STATE__UNKNOWN
        }

    private fun isStateAod(): Boolean = (isAsleepDueToFold() && isAodEnabled)
    private fun getFromState(): Int =
        when {
            isStateAod() -> SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__FROM_STATE__AOD
            isStateScreenOff() ->
                SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__FROM_STATE__SCREEN_OFF
            else -> SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__FROM_STATE__UNKNOWN
        }

    private fun isStateScreenOff(): Boolean = (isAsleepDueToFold() && !isAodEnabled)
    private fun isStateAod() = isAsleep() && isAodEnabled

    private fun isAsleepDueToFold(): Boolean {
        val lastWakefulnessEvent = powerInteractor.detailedWakefulness.value
    private fun isStateScreenOff() = isAsleep() && !isAodEnabled

        return (lastWakefulnessEvent.isAsleep() &&
            (lastWakefulnessEvent.lastSleepReason == WakeSleepReason.FOLD))
    }
    private fun isAsleep() = powerInteractor.detailedWakefulness.value.isAsleep()

    private inline fun log(msg: () -> String) {
        if (DEBUG) Log.d(TAG, msg())
@@ -262,7 +272,7 @@ constructor(
            } else {
                HAS_SCREEN_WAKELOCKS
            }
        return copy(screenWakelockStatus = screenWakelockStatus)
        return copy(screenWakelockStatus = screenWakelockStatus, fromState = getFromState())
    }

    /**
@@ -310,6 +320,7 @@ constructor(

    companion object {
        private const val VALUE_UNKNOWN = -1
        private const val LATENCY_UNDEFINED = -1
        private const val TAG = "DisplaySwitchLatency"
        private val DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE)

+10 −6
Original line number Diff line number Diff line
@@ -72,20 +72,24 @@ sealed interface DisplaySwitchState {

    /**
     * Displays are in a stable state aka not in the process of switching. If we couldn't track
     * display switch properly because end event never arrived within [SCREEN_EVENT_TIMEOUT],
     * [timedOut] is set to true.
     * display switch properly because end event never arrived within
     * [FoldableDisplaySwitchTrackingInteractor.SCREEN_EVENT_TIMEOUT], [timedOut] is set to true.
     */
    data class Idle(override val newDeviceState: DeviceState, val timedOut: Boolean = false) :
        DisplaySwitchState

    /** Displays are currently switching. This state can only come directly after [Idle] state. */
    /**
     * Displays are currently switching. This state can only come directly after [Idle] state.
     * Switching might not be visible to the user, that is, folding device with screen off still
     * emits Switching event as we're swapping default displays.
     */
    data class Switching(override val newDeviceState: DeviceState) : DisplaySwitchState

    /**
     * Switching displays happened multiple times before [Idle] state could settle. This state will
     * hold until no new display switch related events are sent within [COOL_DOWN_DURATION] window.
     * This event can only happen directly after [Switching] state and is always directly followed
     * by [Idle] state.
     * hold until no new display switch related events are sent within
     * [FoldableDisplaySwitchTrackingInteractor.COOL_DOWN_DURATION] window. This event can only
     * happen directly after [Switching] state and is always directly followed by [Idle] state.
     */
    data class Corrupted(override val newDeviceState: DeviceState) : DisplaySwitchState