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

Commit 14240ef3 authored by Matt Pietal's avatar Matt Pietal
Browse files

Fix stuck transitionValue

There is a race condition happening, with an incorrect
transition starting just before another. Because of this,
a prior transition is getting canceled but the
transitionValue is left in a bad state that is very hard
to get out of. Until the race condition can be resolved,
add some cleanup to get the user out of this state.

Bug: 358533338
Test: atest KeyguardTransitionInteractorTest
Flag: EXEMPT bugfix
Change-Id: I20bed404be1664f0a5d553e740e55e9b50133440
parent c2f681cd
Loading
Loading
Loading
Loading
+32 −0
Original line number Diff line number Diff line
@@ -204,6 +204,38 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() {
            assertThat(transitionValues).isEqualTo(listOf(0f, 0.5f, 1f, 1f, 0.5f, 0f))
        }

    @Test
    fun transitionValue_badTransitionResetsTransitionValue() =
        testScope.runTest {
            resetTransitionValueReplayCache(setOf(AOD, DOZING, LOCKSCREEN))
            val transitionValues by collectValues(underTest.transitionValue(state = DOZING))

            val toSteps =
                listOf(
                    TransitionStep(AOD, DOZING, 0f, STARTED),
                    TransitionStep(AOD, DOZING, 0.5f, RUNNING),
                )
            toSteps.forEach {
                repository.sendTransitionStep(it)
                runCurrent()
            }

            // This is an intentionally bad sequence that will leave the transitionValue for
            // DOZING in a bad place, since no CANCELED will be issued for DOZING
            val fromSteps =
                listOf(
                    TransitionStep(AOD, LOCKSCREEN, 0f, STARTED),
                    TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING),
                    TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED),
                )
            fromSteps.forEach {
                repository.sendTransitionStep(it)
                runCurrent()
            }

            assertThat(transitionValues).isEqualTo(listOf(0f, 0.5f, 0f))
        }

    @Test
    fun transitionValue_canceled_toAnotherState() =
        testScope.runTest {
+1 −1
Original line number Diff line number Diff line
@@ -135,7 +135,7 @@ constructor(
        }
    }

    private suspend fun FromOccludedTransitionInteractor.startTransitionToLockscreenOrHub(
    private suspend fun startTransitionToLockscreenOrHub(
        isIdleOnCommunal: Boolean,
        showCommunalFromOccluded: Boolean,
        dreamFromOccluded: Boolean,
+19 −0
Original line number Diff line number Diff line
@@ -162,6 +162,25 @@ constructor(
                }
            }
        }

        // Safety: When any transition is FINISHED, ensure all other transitionValue flows other
        // than the FINISHED state are reset to a value of 0f. There have been rare but severe
        // bugs that get the device stuck in a bad state when these are not properly reset.
        scope.launch {
            repository.transitions
                .filter { it.transitionState == TransitionState.FINISHED }
                .collect {
                    for (state in KeyguardState.entries) {
                        if (state != it.to) {
                            val flow = getTransitionValueFlow(state)
                            val replayCache = flow.replayCache
                            if (!replayCache.isEmpty() && replayCache.last() != 0f) {
                                flow.emit(0f)
                            }
                        }
                    }
                }
        }
    }

    fun transition(edge: Edge, edgeWithoutSceneContainer: Edge): Flow<TransitionStep> {