Loading packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt +37 −11 Original line number Diff line number Diff line Loading @@ -29,25 +29,31 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.power.shared.model.ScreenPowerState import com.android.systemui.power.shared.model.WakeSleepReason import com.android.systemui.power.shared.model.WakefulnessState import com.android.systemui.shared.system.SysUiStatsLog import com.android.systemui.unfold.DisplaySwitchLatencyTracker.DisplaySwitchLatencyEvent import com.android.systemui.unfold.dagger.UnfoldSingleThreadBg import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor import com.android.systemui.util.Compile import com.android.systemui.util.Utils.isDeviceFoldable import com.android.systemui.util.animation.data.repository.AnimationStatusRepository import com.android.systemui.util.kotlin.pairwise import com.android.systemui.util.kotlin.race import com.android.systemui.util.time.SystemClock import com.android.systemui.util.time.measureTimeMillis import java.time.Duration import java.util.concurrent.Executor import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flow import kotlinx.coroutines.launch import kotlinx.coroutines.withTimeout /** * [DisplaySwitchLatencyTracker] tracks latency and related fields for display switch of a foldable Loading @@ -70,6 +76,8 @@ constructor( ) : CoreStartable { private val backgroundDispatcher = singleThreadBgExecutor.asCoroutineDispatcher() private val isAodEnabled: Boolean get() = keyguardInteractor.isAodAvailable.value @OptIn(ExperimentalCoroutinesApi::class) override fun start() { Loading Loading @@ -98,10 +106,16 @@ constructor( val displaySwitchTimeMs = measureTimeMillis(systemClock) { try { withTimeout(SCREEN_EVENT_TIMEOUT) { traceAsync(TAG, "displaySwitch") { waitForDisplaySwitch(toFoldableDeviceState) } } } catch (e: TimeoutCancellationException) { Log.e(TAG, "Wait for display switch timed out") } } displaySwitchLatencyEvent = displaySwitchLatencyEvent.withAfterFields( Loading Loading @@ -129,19 +143,19 @@ constructor( val isTransitionEnabled = unfoldTransitionInteractor.isAvailable && animationStatusRepository.areAnimationsEnabled().first() if (shouldWaitForScreenOn(toFoldableDeviceState, isTransitionEnabled)) { waitForScreenTurnedOn() } else { if (shouldWaitForTransitionStart(toFoldableDeviceState, isTransitionEnabled)) { traceAsync(TAG, "waitForTransitionStart()") { unfoldTransitionInteractor.waitForTransitionStart() } } else { race({ waitForScreenTurnedOn() }, { waitForGoToSleepWithScreenOff() }) } } private fun shouldWaitForScreenOn( private fun shouldWaitForTransitionStart( toFoldableDeviceState: Int, isTransitionEnabled: Boolean ): Boolean = (toFoldableDeviceState == FOLDABLE_DEVICE_STATE_CLOSED || !isTransitionEnabled) ): Boolean = (toFoldableDeviceState != FOLDABLE_DEVICE_STATE_CLOSED && isTransitionEnabled) private suspend fun waitForScreenTurnedOn() { traceAsync(TAG, "waitForScreenTurnedOn()") { Loading @@ -149,19 +163,30 @@ constructor( } } private suspend fun waitForGoToSleepWithScreenOff() { traceAsync(TAG, "waitForGoToSleepWithScreenOff()") { powerInteractor.detailedWakefulness .filter { it.internalWakefulnessState == WakefulnessState.ASLEEP && !isAodEnabled } .first() } } private fun getCurrentState(): Int = 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 { private fun isStateAod(): Boolean = (isAsleepDueToFold() && isAodEnabled) private fun isStateScreenOff(): Boolean = (isAsleepDueToFold() && !isAodEnabled) private fun isAsleepDueToFold(): Boolean { val lastWakefulnessEvent = powerInteractor.detailedWakefulness.value val isAodEnabled = keyguardInteractor.isAodAvailable.value return (lastWakefulnessEvent.isAsleep() && (lastWakefulnessEvent.lastSleepReason == WakeSleepReason.FOLD) && isAodEnabled) (lastWakefulnessEvent.lastSleepReason == WakeSleepReason.FOLD)) } private inline fun log(msg: () -> String) { Loading Loading @@ -232,6 +257,7 @@ constructor( private const val VALUE_UNKNOWN = -1 private const val TAG = "DisplaySwitchLatency" private val DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE) private val SCREEN_EVENT_TIMEOUT = Duration.ofMillis(15000).toMillis() private const val FOLDABLE_DEVICE_STATE_UNKNOWN = SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__FROM_FOLDABLE_DEVICE_STATE__STATE_UNKNOWN Loading packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt +34 −0 Original line number Diff line number Diff line Loading @@ -321,4 +321,38 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() { verify(displaySwitchLatencyLogger, never()).log(any()) } } @Test fun foldToScreenOff_capturesToStateAsScreenOff() { testScope.runTest { areAnimationEnabled.emit(true) deviceState.emit(DeviceState.UNFOLDED) isAodAvailable.emit(false) displaySwitchLatencyTracker.start() deviceState.emit(DeviceState.HALF_FOLDED) systemClock.advanceTime(50) runCurrent() deviceState.emit(DeviceState.FOLDED) lastWakefulnessEvent.emit( WakefulnessModel( internalWakefulnessState = WakefulnessState.ASLEEP, lastSleepReason = WakeSleepReason.FOLD ) ) screenPowerState.emit(ScreenPowerState.SCREEN_OFF) runCurrent() verify(displaySwitchLatencyLogger).log(capture(loggerArgumentCaptor)) val loggedEvent = loggerArgumentCaptor.value val expectedLoggedEvent = DisplaySwitchLatencyEvent( latencyMs = 0, fromFoldableDeviceState = FOLDABLE_DEVICE_STATE_HALF_OPEN, toFoldableDeviceState = FOLDABLE_DEVICE_STATE_CLOSED, toState = SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TO_STATE__SCREEN_OFF ) assertThat(loggedEvent).isEqualTo(expectedLoggedEvent) } } } Loading
packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt +37 −11 Original line number Diff line number Diff line Loading @@ -29,25 +29,31 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.power.shared.model.ScreenPowerState import com.android.systemui.power.shared.model.WakeSleepReason import com.android.systemui.power.shared.model.WakefulnessState import com.android.systemui.shared.system.SysUiStatsLog import com.android.systemui.unfold.DisplaySwitchLatencyTracker.DisplaySwitchLatencyEvent import com.android.systemui.unfold.dagger.UnfoldSingleThreadBg import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor import com.android.systemui.util.Compile import com.android.systemui.util.Utils.isDeviceFoldable import com.android.systemui.util.animation.data.repository.AnimationStatusRepository import com.android.systemui.util.kotlin.pairwise import com.android.systemui.util.kotlin.race import com.android.systemui.util.time.SystemClock import com.android.systemui.util.time.measureTimeMillis import java.time.Duration import java.util.concurrent.Executor import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flow import kotlinx.coroutines.launch import kotlinx.coroutines.withTimeout /** * [DisplaySwitchLatencyTracker] tracks latency and related fields for display switch of a foldable Loading @@ -70,6 +76,8 @@ constructor( ) : CoreStartable { private val backgroundDispatcher = singleThreadBgExecutor.asCoroutineDispatcher() private val isAodEnabled: Boolean get() = keyguardInteractor.isAodAvailable.value @OptIn(ExperimentalCoroutinesApi::class) override fun start() { Loading Loading @@ -98,10 +106,16 @@ constructor( val displaySwitchTimeMs = measureTimeMillis(systemClock) { try { withTimeout(SCREEN_EVENT_TIMEOUT) { traceAsync(TAG, "displaySwitch") { waitForDisplaySwitch(toFoldableDeviceState) } } } catch (e: TimeoutCancellationException) { Log.e(TAG, "Wait for display switch timed out") } } displaySwitchLatencyEvent = displaySwitchLatencyEvent.withAfterFields( Loading Loading @@ -129,19 +143,19 @@ constructor( val isTransitionEnabled = unfoldTransitionInteractor.isAvailable && animationStatusRepository.areAnimationsEnabled().first() if (shouldWaitForScreenOn(toFoldableDeviceState, isTransitionEnabled)) { waitForScreenTurnedOn() } else { if (shouldWaitForTransitionStart(toFoldableDeviceState, isTransitionEnabled)) { traceAsync(TAG, "waitForTransitionStart()") { unfoldTransitionInteractor.waitForTransitionStart() } } else { race({ waitForScreenTurnedOn() }, { waitForGoToSleepWithScreenOff() }) } } private fun shouldWaitForScreenOn( private fun shouldWaitForTransitionStart( toFoldableDeviceState: Int, isTransitionEnabled: Boolean ): Boolean = (toFoldableDeviceState == FOLDABLE_DEVICE_STATE_CLOSED || !isTransitionEnabled) ): Boolean = (toFoldableDeviceState != FOLDABLE_DEVICE_STATE_CLOSED && isTransitionEnabled) private suspend fun waitForScreenTurnedOn() { traceAsync(TAG, "waitForScreenTurnedOn()") { Loading @@ -149,19 +163,30 @@ constructor( } } private suspend fun waitForGoToSleepWithScreenOff() { traceAsync(TAG, "waitForGoToSleepWithScreenOff()") { powerInteractor.detailedWakefulness .filter { it.internalWakefulnessState == WakefulnessState.ASLEEP && !isAodEnabled } .first() } } private fun getCurrentState(): Int = 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 { private fun isStateAod(): Boolean = (isAsleepDueToFold() && isAodEnabled) private fun isStateScreenOff(): Boolean = (isAsleepDueToFold() && !isAodEnabled) private fun isAsleepDueToFold(): Boolean { val lastWakefulnessEvent = powerInteractor.detailedWakefulness.value val isAodEnabled = keyguardInteractor.isAodAvailable.value return (lastWakefulnessEvent.isAsleep() && (lastWakefulnessEvent.lastSleepReason == WakeSleepReason.FOLD) && isAodEnabled) (lastWakefulnessEvent.lastSleepReason == WakeSleepReason.FOLD)) } private inline fun log(msg: () -> String) { Loading Loading @@ -232,6 +257,7 @@ constructor( private const val VALUE_UNKNOWN = -1 private const val TAG = "DisplaySwitchLatency" private val DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE) private val SCREEN_EVENT_TIMEOUT = Duration.ofMillis(15000).toMillis() private const val FOLDABLE_DEVICE_STATE_UNKNOWN = SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__FROM_FOLDABLE_DEVICE_STATE__STATE_UNKNOWN Loading
packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt +34 −0 Original line number Diff line number Diff line Loading @@ -321,4 +321,38 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() { verify(displaySwitchLatencyLogger, never()).log(any()) } } @Test fun foldToScreenOff_capturesToStateAsScreenOff() { testScope.runTest { areAnimationEnabled.emit(true) deviceState.emit(DeviceState.UNFOLDED) isAodAvailable.emit(false) displaySwitchLatencyTracker.start() deviceState.emit(DeviceState.HALF_FOLDED) systemClock.advanceTime(50) runCurrent() deviceState.emit(DeviceState.FOLDED) lastWakefulnessEvent.emit( WakefulnessModel( internalWakefulnessState = WakefulnessState.ASLEEP, lastSleepReason = WakeSleepReason.FOLD ) ) screenPowerState.emit(ScreenPowerState.SCREEN_OFF) runCurrent() verify(displaySwitchLatencyLogger).log(capture(loggerArgumentCaptor)) val loggedEvent = loggerArgumentCaptor.value val expectedLoggedEvent = DisplaySwitchLatencyEvent( latencyMs = 0, fromFoldableDeviceState = FOLDABLE_DEVICE_STATE_HALF_OPEN, toFoldableDeviceState = FOLDABLE_DEVICE_STATE_CLOSED, toState = SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TO_STATE__SCREEN_OFF ) assertThat(loggedEvent).isEqualTo(expectedLoggedEvent) } } }