diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java index a14aa52225802..f282aaefe0fd7 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java @@ -221,7 +221,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(); @@ -267,7 +274,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(""); @@ -292,9 +299,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()); - mWindowManager.addView(mWindow.getDecorView(), mWindow.getAttributes()); + // 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() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java index 9d4bef621988c..9e70c27f7881f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java @@ -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; @@ -231,6 +232,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();