From 60f9c97a4893644dde4cff4e595091afce5fb099 Mon Sep 17 00:00:00 2001 From: Jorim Jaggi Date: Thu, 1 Feb 2018 19:21:07 +0100 Subject: [PATCH] Transfer starting windows more aggressively We currently transfer starting windows when starting another activity on top of the previous one. However, that's not enough: Sometimes we also need to transfer back in case the one on top finishes itself/gets hidden for other reasons. This fixes an issue where some animations are aborted in the middle of the animation because the trampoline activity contained the starting window but then the trampoline activity was finished. Test: go/wm-smoke Test: Open Drive, Reopen Drive 100x Test: Open Hangouts, Receive notification, open it, reopen hangouts from launcher 100x Test: AppWindowContainerControllerTests Bug: 72301120 Change-Id: I6711ce0bc35de430aac03f7b8b39ceac26e595e7 --- .../wm/AppWindowContainerController.java | 2 ++ .../com/android/server/wm/AppWindowToken.java | 20 +++++++++++ .../server/wm/WindowSurfacePlacer.java | 3 +- .../wm/AppWindowContainerControllerTests.java | 34 ++++++++++++++++--- 4 files changed, 53 insertions(+), 6 deletions(-) diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java index 40f772aaa529e..1170148d67819 100644 --- a/services/core/java/com/android/server/wm/AppWindowContainerController.java +++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java @@ -379,6 +379,8 @@ public class AppWindowContainerController if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "No longer Stopped: " + wtoken); wtoken.mAppStopped = false; + + mContainer.transferStartingWindowFromHiddenAboveTokenIfNeeded(); } // If we are preparing an app transition, then delay changing diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 267233750f553..b2f153a74699a 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -187,6 +187,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree StartingSurface startingSurface; boolean startingDisplayed; boolean startingMoved; + // True if the hidden state of this token was forced to false due to a transferred starting // window. private boolean mHiddenSetFromTransferredStartingWindow; @@ -1136,6 +1137,25 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree stopFreezingScreen(true, true); } + /** + * Tries to transfer the starting window from a token that's above ourselves in the task but + * not visible anymore. This is a common scenario apps use: Trampoline activity T start main + * activity M in the same task. Now, when reopening the task, T starts on top of M but then + * immediately finishes after, so we have to transfer T to M. + */ + void transferStartingWindowFromHiddenAboveTokenIfNeeded() { + final Task task = getTask(); + for (int i = task.mChildren.size() - 1; i >= 0; i--) { + final AppWindowToken fromToken = task.mChildren.get(i); + if (fromToken == this) { + return; + } + if (fromToken.hiddenRequested && transferStartingWindow(fromToken.token)) { + return; + } + } + } + boolean transferStartingWindow(IBinder transferFrom) { final AppWindowToken fromToken = getDisplayContent().getAppWindowToken(transferFrom); if (fromToken == null) { diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java index 417959087de2c..40ee55277302e 100644 --- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java +++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java @@ -559,7 +559,8 @@ class WindowSurfacePlacer { + wtoken.allDrawn + " startingDisplayed=" + wtoken.startingDisplayed + " startingMoved=" + wtoken.startingMoved + " isRelaunching()=" - + wtoken.isRelaunching()); + + wtoken.isRelaunching() + " startingWindow=" + + wtoken.startingWindow); final boolean allDrawn = wtoken.allDrawn && !wtoken.isRelaunching(); diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java index 76e4e895278ea..e0645b1f4bfb0 100644 --- a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java @@ -20,11 +20,9 @@ import android.support.test.filters.FlakyTest; import org.junit.Test; import android.platform.test.annotations.Presubmit; -import android.platform.test.annotations.SecurityTest; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; -import android.view.WindowManager; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; @@ -36,13 +34,12 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; -import java.util.function.Consumer; +import com.android.server.wm.WindowTestUtils.TestTaskWindowContainerController; /** * Test class for {@link AppWindowContainerController}. * - * Build/Install/Run: - * bit FrameworksServicesTests:com.android.server.wm.AppWindowContainerControllerTests + * atest FrameworksServicesTests:com.android.server.wm.AppWindowContainerControllerTests */ @SmallTest @Presubmit @@ -175,6 +172,33 @@ public class AppWindowContainerControllerTests extends WindowTestsBase { assertHasStartingWindow(controller2.getAppWindowToken(mDisplayContent)); } + @Test + public void testTryTransferStartingWindowFromHiddenAboveToken() throws Exception { + + // Add two tasks on top of each other. + TestTaskWindowContainerController taskController = + new WindowTestUtils.TestTaskWindowContainerController(this); + final WindowTestUtils.TestAppWindowContainerController controllerTop = + createAppWindowController(taskController); + final WindowTestUtils.TestAppWindowContainerController controllerBottom = + createAppWindowController(taskController); + + // Add a starting window. + controllerTop.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(), + android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, + false, false); + waitUntilHandlersIdle(); + + // Make the top one invisible, and try transfering the starting window from the top to the + // bottom one. + controllerTop.setVisibility(false, false); + controllerBottom.mContainer.transferStartingWindowFromHiddenAboveTokenIfNeeded(); + + // Assert that the bottom window now has the starting window. + assertNoStartingWindow(controllerTop.getAppWindowToken(mDisplayContent)); + assertHasStartingWindow(controllerBottom.getAppWindowToken(mDisplayContent)); + } + @Test public void testReparent() throws Exception { final StackWindowController stackController =