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

Commit b0514b19 authored by Bryce Lee's avatar Bryce Lee
Browse files

Handle invalid dream window in DreamOverlayService.

It is possible for a dream's window to become invalid shortly after
starting. For example, another dream might begin almost immediately
following. In these cases, it is possible that the DreamOverlayService
might not process the start event until after the dream window becomes
invalid. If not handled properly, an BadTokenException will cause
SystemUI to crash.

This changelist addresses the issue by catching the exception and
tearing down the overlay.

Test: atest DreamOverlayServiceTest
Fixes: 268209056
Change-Id: I25ff6ff58e6a612ea609a4541219bea149eeace9
parent 6d61c09d
Loading
Loading
Loading
Loading
+23 −3
Original line number Diff line number Diff line
@@ -195,7 +195,14 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
            mDreamOverlayTouchMonitor.init();

            mStateController.setShouldShowComplications(shouldShowComplications());
            addOverlayWindowLocked(layoutParams);

            // If we are not able to add the overlay window, reset the overlay.
            if (!addOverlayWindowLocked(layoutParams)) {
                resetCurrentDreamOverlayLocked();
                return;
            }


            setCurrentStateLocked(Lifecycle.State.RESUMED);
            mStateController.setOverlayActive(true);
            final ComponentName dreamComponent = getDreamComponent();
@@ -241,7 +248,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
     * @param layoutParams The {@link android.view.WindowManager.LayoutParams} which allow inserting
     *                     into the dream window.
     */
    private void addOverlayWindowLocked(WindowManager.LayoutParams layoutParams) {
    private boolean addOverlayWindowLocked(WindowManager.LayoutParams layoutParams) {
        mWindow = new PhoneWindow(mContext);
        // Default to SystemUI name for TalkBack.
        mWindow.setTitle("");
@@ -266,9 +273,22 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
        // risk an IllegalStateException in some cases when setting the container view as the
        // window's content view and the container view hasn't been properly removed previously).
        removeContainerViewFromParentLocked();

        mWindow.setContentView(mDreamOverlayContainerViewController.getContainerView());

        // It is possible that a dream's window (and the dream as a whole) is no longer valid by
        // the time the overlay service processes the dream. This can happen for example if
        // another dream is started immediately after the existing dream begins. In this case, the
        // overlay service should identify the situation through the thrown exception and tear down
        // the overlay.
        try {
            mWindowManager.addView(mWindow.getDecorView(), mWindow.getAttributes());
            return true;
        } catch (WindowManager.BadTokenException exception) {
            Log.e(TAG, "Dream activity window invalid: " + layoutParams.packageName,
                    exception);
            return false;
        }
    }

    private void removeContainerViewFromParentLocked() {
+26 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -190,6 +191,31 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
        verify(mWindowManager).addView(any(), any());
    }

    // Validates that {@link DreamOverlayService} properly handles the case where the dream's
    // window is no longer valid by the time start is called.
    @Test
    public void testInvalidWindowAddStart() throws Exception {
        final IDreamOverlayClient client = getClient();

        doThrow(new WindowManager.BadTokenException()).when(mWindowManager).addView(any(), any());
        // Inform the overlay service of dream starting.
        client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
                false /*shouldShowComplication*/);
        mMainExecutor.runAllReady();

        verify(mWindowManager).addView(any(), any());

        verify(mStateController).setOverlayActive(false);
        verify(mStateController).setLowLightActive(false);
        verify(mStateController).setEntryAnimationsFinished(false);

        verify(mStateController, never()).setOverlayActive(true);
        verify(mUiEventLogger, never()).log(
                DreamOverlayService.DreamOverlayEvent.DREAM_OVERLAY_COMPLETE_START);

        verify(mDreamOverlayCallbackController, never()).onStartDream();
    }

    @Test
    public void testDreamOverlayContainerViewControllerInitialized() throws Exception {
        final IDreamOverlayClient client = getClient();