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

Commit 625d3011 authored by William Xiao's avatar William Xiao
Browse files

Fix crash when reading isDreamInPreviewMode

Prior to this fix, callbacks could still trigger between onEndDream,
which is when isDreamInPreviewMode is no longer valid to call, and the
overlay exit animations finishing. This CL adds a new ended state that
is checked before executing code that relies on a connected dream See
bug for a more detailed description of the issue and fix.

Bug: 432039567
Fixes: 432039567
Test: atest DreamOverlayServiceTest
Flag: android.service.dreams.dream_overlay_started_fix
Change-Id: Ib04af1508df08507737a490af1d0f6ee33d0473b
parent ccee9a90
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -150,3 +150,14 @@ flag {
        purpose: PURPOSE_BUGFIX
    }
}


flag {
    name: "dream_overlay_started_fix"
    namespace: "systemui"
    description: "Guards fix for the lifetime of when the dream is considered started"
    bug: "432039567"
    metadata {
        purpose: PURPOSE_BUGFIX
    }
}
 No newline at end of file
+86 −6
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
import android.service.dreams.Flags
import android.service.dreams.Flags.FLAG_DREAM_OVERLAY_STARTED_FIX
import android.service.dreams.IDreamOverlay
import android.service.dreams.IDreamOverlayCallback
import android.service.dreams.IDreamOverlayClient
@@ -40,7 +41,6 @@ import com.android.compose.animation.scene.ObservableTransitionState
import com.android.internal.logging.UiEventLogger
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
import com.android.systemui.Flags.FLAG_DREAM_BIOMETRIC_PROMPT_FIXES
import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
@@ -749,7 +749,7 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() {
    }

    @Test
    @EnableFlags(Flags.FLAG_DREAM_WAKE_REDIRECT, FLAG_COMMUNAL_HUB)
    @EnableFlags(Flags.FLAG_DREAM_WAKE_REDIRECT)
    @DisableFlags(FLAG_SCENE_CONTAINER, FLAG_GLANCEABLE_HUB_V2)
    @kotlin.Throws(RemoteException::class)
    fun testTransitionToGlanceableHub() =
@@ -775,7 +775,7 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() {
        }

    @Test
    @EnableFlags(Flags.FLAG_DREAM_WAKE_REDIRECT, FLAG_SCENE_CONTAINER, FLAG_COMMUNAL_HUB)
    @EnableFlags(Flags.FLAG_DREAM_WAKE_REDIRECT, FLAG_SCENE_CONTAINER)
    @DisableFlags(FLAG_GLANCEABLE_HUB_V2)
    @kotlin.Throws(RemoteException::class)
    fun testTransitionToGlanceableHub_sceneContainer() =
@@ -803,7 +803,7 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() {
        }

    @Test
    @EnableFlags(Flags.FLAG_DREAM_WAKE_REDIRECT, FLAG_COMMUNAL_HUB, FLAG_GLANCEABLE_HUB_V2)
    @EnableFlags(Flags.FLAG_DREAM_WAKE_REDIRECT, FLAG_GLANCEABLE_HUB_V2)
    @Throws(RemoteException::class)
    fun testRedirect_v2Enabled_notTriggered() =
        kosmos.runTest {
@@ -823,7 +823,7 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() {
        }

    @Test
    @EnableFlags(Flags.FLAG_DREAM_WAKE_REDIRECT, FLAG_COMMUNAL_HUB)
    @EnableFlags(Flags.FLAG_DREAM_WAKE_REDIRECT)
    @DisableFlags(FLAG_GLANCEABLE_HUB_V2)
    @Throws(RemoteException::class)
    fun testRedirectExit() =
@@ -1410,6 +1410,86 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() {
            assertThat(gestureRepository.gestureBlockedMatchers.value).isEmpty()
        }

    @EnableFlags(FLAG_DREAM_OVERLAY_STARTED_FIX)
    @Test
    fun testGestureBlocking_dreamEnded_gestureBlockingNotUpdated() =
        kosmos.runTest {
            val client = client

            // Inform the overlay service of dream starting. Do not show dream complications.
            client.startDream(
                mWindowParams,
                mDreamOverlayCallback,
                DREAM_COMPONENT,
                false /*isPreview*/,
                false, /*shouldShowComplication*/
            )
            mMainExecutor.runAllReady()

            val callbackCaptor = argumentCaptor<KeyguardUpdateMonitorCallback>()
            verify(mKeyguardUpdateMonitor).registerCallback(callbackCaptor.capture())

            // Gesture are blocked to start.
            assertThat(gestureRepository.gestureBlockedMatchers.value).hasSize(1)

            // Trigger dream end, but delay the reset.
            whenever(mStateController.areExitAnimationsRunning()).thenReturn(true)
            client.endDream()
            mMainExecutor.runAllReady()

            // Shade is shown.
            callbackCaptor.firstValue.onShadeExpandedChanged(true)
            mMainExecutor.runAllReady()

            // Gesture blocking not updated since dream is already ended.
            assertThat(gestureRepository.gestureBlockedMatchers.value).hasSize(1)

            // Finish exit animations, end dream.
            whenever(mStateController.areExitAnimationsRunning()).thenReturn(false)
            val stateCallbackCaptor = argumentCaptor<DreamOverlayStateController.Callback>()
            verify(mStateController).addCallback(stateCallbackCaptor.capture())
            stateCallbackCaptor.lastValue.onStateChanged()
            mMainExecutor.runAllReady()

            // Gesture blocking removed on dream end.
            assertThat(gestureRepository.gestureBlockedMatchers.value).isEmpty()
        }

    @DisableFlags(FLAG_DREAM_OVERLAY_STARTED_FIX)
    @Test
    fun testGestureBlocking_dreamEnded_gestureBlockingUpdated() =
        kosmos.runTest {
            val client = client

            // Inform the overlay service of dream starting. Do not show dream complications.
            client.startDream(
                mWindowParams,
                mDreamOverlayCallback,
                DREAM_COMPONENT,
                false /*isPreview*/,
                false, /*shouldShowComplication*/
            )
            mMainExecutor.runAllReady()

            val callbackCaptor = argumentCaptor<KeyguardUpdateMonitorCallback>()
            verify(mKeyguardUpdateMonitor).registerCallback(callbackCaptor.capture())

            // Gesture are blocked to start.
            assertThat(gestureRepository.gestureBlockedMatchers.value).hasSize(1)

            // Trigger dream end, but delay the reset.
            whenever(mStateController.areExitAnimationsRunning()).thenReturn(true)
            client.endDream()
            mMainExecutor.runAllReady()

            // Shade is shown.
            callbackCaptor.firstValue.onShadeExpandedChanged(true)
            mMainExecutor.runAllReady()

            // Gesture blocking is still updated as the reset has not happened yet.
            assertThat(gestureRepository.gestureBlockedMatchers.value).isEmpty()
        }

    @EnableFlags(FLAG_DREAM_BIOMETRIC_PROMPT_FIXES)
    @Test
    fun testBiometricPromptShowing_setsLifecycleState() =
@@ -1575,8 +1655,8 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() {
        @Parameters(name = "{0}")
        fun getParams(): List<FlagsParameterization> {
            return FlagsParameterization.allCombinationsOf(
                    FLAG_COMMUNAL_HUB,
                    FLAG_GLANCEABLE_HUB_V2,
                    FLAG_DREAM_OVERLAY_STARTED_FIX,
                )
                .andSceneContainer()
        }
+24 −4
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.systemui.dreams;

import static android.service.dreams.Flags.dreamOverlayStartedFix;
import static android.service.dreams.Flags.dreamWakeRedirect;
import static android.service.dreams.Flags.dreamsV2;

@@ -134,9 +135,19 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
    // A reference to the {@link Window} used to hold the dream overlay.
    private Window mWindow;

    // True if a dream has bound to the service and dream overlay service has started.
    /**
     * True if a dream has bound to the service and dream overlay service has started. Does not
     * immediately flip to false in {@link #onEndDream()}, waits until the overlay service state is
     * reset.
     */
    private boolean mStarted = false;

    /**
     * True if the connected dream has been ended from {@link #onEndDream()} and has not fully
     * started yet.
     */
    private boolean mEnded = false;

    // True if the service has been destroyed.
    private boolean mDestroyed = false;

@@ -602,6 +613,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ

        mDreamOverlayCallbackController.onStartDream();
        mStarted = true;
        mEnded = false;

        mKeyguardUpdateMonitor.registerCallback(mKeyguardCallback);

@@ -626,6 +638,9 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ

    @Override
    public void onEndDream() {
        if (dreamOverlayStartedFix()) {
            mEnded = true;
        }
        mResetHandler.reset("ending dream");
    }

@@ -644,8 +659,13 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
        }
    }

    /**
     * Update the back gesture blocking state. Should only be called from
     * {@link #dreamScopedExecute(Runnable, String)}.
     */
    private void updateGestureBlockingLocked() {
        final boolean shouldBlock = mStarted && !mShadeExpanded && !mBouncerShowing
        final boolean shouldBlock =
                (dreamOverlayStartedFix() || mStarted) && !mShadeExpanded && !mBouncerShowing
                        && !isDreamInPreviewMode() && !mBiometricPromptShowing;

        if (shouldBlock) {
@@ -809,7 +829,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ

    private void dreamScopedExecute(Runnable runnable, String description) {
        mExecutor.execute(() -> {
            if (!mStarted) {
            if (!mStarted || mEnded) {
                Log.d(TAG, "could not execute when not dreaming:" + description);
                return;
            }