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
This commit is contained in:
Jorim Jaggi
2018-02-01 19:21:07 +01:00
parent 89c4bd5f26
commit 60f9c97a48
4 changed files with 53 additions and 6 deletions

View File

@@ -379,6 +379,8 @@ public class AppWindowContainerController
if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "No longer Stopped: " + wtoken); if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "No longer Stopped: " + wtoken);
wtoken.mAppStopped = false; wtoken.mAppStopped = false;
mContainer.transferStartingWindowFromHiddenAboveTokenIfNeeded();
} }
// If we are preparing an app transition, then delay changing // If we are preparing an app transition, then delay changing

View File

@@ -187,6 +187,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
StartingSurface startingSurface; StartingSurface startingSurface;
boolean startingDisplayed; boolean startingDisplayed;
boolean startingMoved; boolean startingMoved;
// True if the hidden state of this token was forced to false due to a transferred starting // True if the hidden state of this token was forced to false due to a transferred starting
// window. // window.
private boolean mHiddenSetFromTransferredStartingWindow; private boolean mHiddenSetFromTransferredStartingWindow;
@@ -1136,6 +1137,25 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
stopFreezingScreen(true, true); 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) { boolean transferStartingWindow(IBinder transferFrom) {
final AppWindowToken fromToken = getDisplayContent().getAppWindowToken(transferFrom); final AppWindowToken fromToken = getDisplayContent().getAppWindowToken(transferFrom);
if (fromToken == null) { if (fromToken == null) {

View File

@@ -559,7 +559,8 @@ class WindowSurfacePlacer {
+ wtoken.allDrawn + " startingDisplayed=" + wtoken.allDrawn + " startingDisplayed="
+ wtoken.startingDisplayed + " startingMoved=" + wtoken.startingDisplayed + " startingMoved="
+ wtoken.startingMoved + " isRelaunching()=" + wtoken.startingMoved + " isRelaunching()="
+ wtoken.isRelaunching()); + wtoken.isRelaunching() + " startingWindow="
+ wtoken.startingWindow);
final boolean allDrawn = wtoken.allDrawn && !wtoken.isRelaunching(); final boolean allDrawn = wtoken.allDrawn && !wtoken.isRelaunching();

View File

@@ -20,11 +20,9 @@ import android.support.test.filters.FlakyTest;
import org.junit.Test; import org.junit.Test;
import android.platform.test.annotations.Presubmit; import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.SecurityTest;
import android.support.test.InstrumentationRegistry; import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest; import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4; 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_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; 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.assertNull;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import java.util.function.Consumer; import com.android.server.wm.WindowTestUtils.TestTaskWindowContainerController;
/** /**
* Test class for {@link AppWindowContainerController}. * Test class for {@link AppWindowContainerController}.
* *
* Build/Install/Run: * atest FrameworksServicesTests:com.android.server.wm.AppWindowContainerControllerTests
* bit FrameworksServicesTests:com.android.server.wm.AppWindowContainerControllerTests
*/ */
@SmallTest @SmallTest
@Presubmit @Presubmit
@@ -175,6 +172,33 @@ public class AppWindowContainerControllerTests extends WindowTestsBase {
assertHasStartingWindow(controller2.getAppWindowToken(mDisplayContent)); 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 @Test
public void testReparent() throws Exception { public void testReparent() throws Exception {
final StackWindowController stackController = final StackWindowController stackController =