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
This commit is contained in:
Bryce Lee
2023-02-21 17:23:52 -08:00
parent 6d61c09d36
commit b0514b19d8
2 changed files with 49 additions and 3 deletions

View File

@@ -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());
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() {

View File

@@ -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();