diff options
| author | 2017-01-26 10:55:34 -0800 | |
|---|---|---|
| committer | 2017-01-27 17:23:57 +0000 | |
| commit | 3048004087968674c3af97e1c0b110a223f04703 (patch) | |
| tree | c3413c07df99b65c1395e46f57eed21ab779eb99 | |
| parent | 1457cdcca56985d8aa09846ad53ee459997ea56e (diff) | |
Reparent the activity in the task with associated window containers.
- Currently moveActivityToStack() adds the activity to the top of the
new task in the new stack, but the code path to update the window
container controllers assumes that they are in the same task and
attempts to position it by index. Instead, we should properly
reparent the activity in the new task (just like we reparent tasks
into new stacks), and also reparent the associated app window tokens
into their new tasks on the WM side.
- Also should fix an issue when trying to reparent an activity from one
task to another task by affinity.
Bug: 34394702
Test: AppWidgetContainerControllerTests#testReparent
Test: android.server.cts.ActivityManagerPinnedStackTests#testEnterPipFromTaskWithMultipleActivities
Change-Id: Ib767f85eb4f469cfd1c108c55242996325bdb866
7 files changed, 157 insertions, 10 deletions
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 8ca77c50b9e1..baf777220691 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -729,7 +729,6 @@ final class ActivityRecord implements AppWindowContainerListener { if (mWindowContainerController != null) { throw new IllegalArgumentException("Window container=" + mWindowContainerController + " already created for r=" + this); - } inHistory = true; @@ -754,6 +753,26 @@ final class ActivityRecord implements AppWindowContainerListener { mWindowContainerController = null; } + /** + * Reparents this activity into {@param newTask} at the provided {@param position}. The caller + * should ensure that the {@param newTask} is not already the parent of this activity. + */ + void reparent(TaskRecord newTask, int position, String reason) { + final TaskRecord prevTask = task; + if (prevTask == newTask) { + throw new IllegalArgumentException(reason + ": task=" + newTask + + " is already the parent of r=" + this); + } + + // Must reparent first in window manager + mWindowContainerController.reparent(newTask.getWindowContainerController(), position); + + // Remove the activity from the old task and add it to the new task + prevTask.removeActivity(this); + setTask(newTask, null); + newTask.addActivityAtIndex(position, this); + } + private boolean isHomeIntent(Intent intent) { return Intent.ACTION_MAIN.equals(intent.getAction()) && intent.hasCategory(Intent.CATEGORY_HOME) diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 98b5835c07e3..e479f85ab614 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -3043,8 +3043,7 @@ final class ActivityStack extends ConfigurationContainer implements StackWindowL + " to task=" + task + ":" + taskInsertionPoint); for (int srcPos = start; srcPos >= i; --srcPos) { final ActivityRecord p = activities.get(srcPos); - p.setTask(task, null); - task.addActivityAtIndex(taskInsertionPoint, p); + p.reparent(task, taskInsertionPoint, "resetAffinityTaskIfNeededLocked"); if (DEBUG_ADD_REMOVE) Slog.i(TAG_ADD_REMOVE, "Removing and adding activity " + p + " to stack at " + task @@ -5071,12 +5070,17 @@ final class ActivityStack extends ConfigurationContainer implements StackWindowL final boolean wasResumed = wasFocused && (prevStack.mResumedActivity == r); final boolean wasPaused = prevStack.mPausingActivity == r; + // Create a new task for the activity to be parented in final TaskRecord task = createTaskRecord( mStackSupervisor.getNextTaskIdForUserLocked(r.userId), r.info, r.intent, null, null, true, r.mActivityType); - r.setTask(task, null); - task.addActivityToTop(r); + // This is a new task, so reparenting it to position 0 will move it to the top + r.reparent(task, 0 /* position */, "moveActivityToStack"); + + // Notify the task actiivties if it was moved to/from a pinned stack mStackSupervisor.scheduleReportPictureInPictureModeChangedIfNeeded(task, prevStack); + + // Resume the activity if necessary after it has moved moveToFrontAndResumeStateIfNeeded(r, wasFocused, wasResumed, wasPaused, "moveActivityToStack"); if (wasResumed) { diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index f12d7b79a82f..fef4073922c6 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -1028,8 +1028,10 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta addActivityAtIndex(mActivities.size(), r); } - // TODO: Figure-out if any of the call points expect the window container to be reparented and - // correct them to use the right method. + /** + * Adds an activity {@param r} at the given {@param index}. The activity {@param r} must either + * be in the current task or unparented to any task. + */ void addActivityAtIndex(int index, ActivityRecord r) { // Remove r first, and if it wasn't already in the list and it's fullscreen, count it. if (!mActivities.remove(r) && r.fullscreen) { diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java index 27e0f292fb65..3a86874b1f4e 100644 --- a/services/core/java/com/android/server/wm/AppWindowContainerController.java +++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java @@ -41,6 +41,7 @@ import android.util.Slog; import android.view.IApplicationToken; import android.view.WindowManagerPolicy.StartingSurface; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.AttributeCache; /** * Controller for the app window token container. This is created by activity manager to link @@ -202,7 +203,7 @@ public class AppWindowContainerController + " controller=" + taskController); } - atoken = new AppWindowToken(mService, token, voiceInteraction, task.getDisplayContent(), + atoken = createAppWindow(mService, token, voiceInteraction, task.getDisplayContent(), inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdkVersion, requestedOrientation, rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable, this); @@ -212,6 +213,18 @@ public class AppWindowContainerController } } + @VisibleForTesting + AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token, + boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos, + boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation, + int rotationAnimationHint, int configChanges, boolean launchTaskBehind, + boolean alwaysFocusable, AppWindowContainerController controller) { + return new AppWindowToken(service, token, voiceInteraction, dc, + inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, orientation, + rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable, + controller); + } + public void removeContainer(int displayId) { synchronized(mWindowMap) { final DisplayContent dc = mRoot.getDisplayContent(displayId); @@ -230,6 +243,25 @@ public class AppWindowContainerController throw new UnsupportedOperationException("Use removeContainer(displayId) instead."); } + public void reparent(TaskWindowContainerController taskController, int position) { + synchronized (mWindowMap) { + if (DEBUG_ADD_REMOVE) Slog.i(TAG_WM, "reparent: moving app token=" + mToken + + " to task=" + taskController + " at " + position); + if (mContainer == null) { + if (DEBUG_ADD_REMOVE) Slog.i(TAG_WM, + "reparent: could not find app token=" + mToken); + return; + } + final Task task = taskController.mContainer; + if (task == null) { + throw new IllegalArgumentException("reparent: could not find task=" + + taskController); + } + mContainer.reparent(task, position); + mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded(); + } + } + public Configuration setOrientation(int requestedOrientation, int displayId, Configuration displayConfig, boolean freezeScreenIfNeeded) { synchronized(mWindowMap) { diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 079dc8f2b0ad..bcc720d05424 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -850,6 +850,27 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } } + void reparent(Task task, int position) { + if (task == mTask) { + throw new IllegalArgumentException( + "window token=" + this + " already child of task=" + mTask); + } + if (DEBUG_ADD_REMOVE) Slog.i(TAG, "reParentWindowToken: removing window token=" + this + + " from task=" + mTask); + final DisplayContent prevDisplayContent = getDisplayContent(); + + getParent().removeChild(this); + task.addChild(this, position); + + // Relayout display(s). + final DisplayContent displayContent = task.getDisplayContent(); + displayContent.setLayoutNeeded(); + if (prevDisplayContent != displayContent) { + onDisplayChanged(displayContent); + prevDisplayContent.setLayoutNeeded(); + } + } + private boolean canFreezeBounds() { // For freeform windows, we can't freeze the bounds at the moment because this would make // the resizing unresponsive. 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 2af4163770ae..04e55836a706 100644 --- a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java @@ -29,6 +29,7 @@ import static android.content.res.Configuration.EMPTY; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; /** * Test class for {@link AppWindowContainerController}. @@ -135,9 +136,50 @@ public class AppWindowContainerControllerTests extends WindowTestsBase { assertHasStartingWindow(controller2.getAppWindowToken()); } + @Test + public void testReparent() throws Exception { + final TestTaskWindowContainerController taskController1 = + new TestTaskWindowContainerController( + createStackControllerOnDisplay(sDisplayContent)); + final TestAppWindowContainerController appWindowController1 = createAppWindowController( + taskController1); + final TestTaskWindowContainerController taskController2 = + new TestTaskWindowContainerController( + createStackControllerOnDisplay(sDisplayContent)); + final TestAppWindowContainerController appWindowController2 = createAppWindowController( + taskController2); + final TestTaskWindowContainerController taskController3 = + new TestTaskWindowContainerController( + createStackControllerOnDisplay(sDisplayContent)); + + try { + appWindowController1.reparent(taskController1, 0); + fail("Should not be able to reparent to the same parent"); + } catch (IllegalArgumentException e) { + // Expected + } + + try { + taskController3.setContainer(null); + appWindowController1.reparent(taskController3, 0); + fail("Should not be able to reparent to a task that doesn't have a container"); + } catch (IllegalArgumentException e) { + // Expected + } + + // Reparent the app window and ensure that it is moved + appWindowController1.reparent(taskController2, 0); + assertEquals(taskController2.mContainer, appWindowController1.mContainer.getParent()); + assertEquals(0, ((TestAppWindowToken) appWindowController1.mContainer).positionInParent()); + assertEquals(1, ((TestAppWindowToken) appWindowController2.mContainer).positionInParent()); + } + private TestAppWindowContainerController createAppWindowController() { - final TestTaskWindowContainerController taskController = - new TestTaskWindowContainerController(); + return createAppWindowController(new TestTaskWindowContainerController()); + } + + private TestAppWindowContainerController createAppWindowController( + TestTaskWindowContainerController taskController) { return new TestAppWindowContainerController(taskController); } } diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java index 72157b6d1c29..dd4b3440de81 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java @@ -222,6 +222,16 @@ class WindowTestsBase { super(sWm, null, false, dc); } + TestAppWindowToken(WindowManagerService service, IApplicationToken token, + boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos, + boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation, + int rotationAnimationHint, int configChanges, boolean launchTaskBehind, + boolean alwaysFocusable, AppWindowContainerController controller) { + super(service, token, voiceInteraction, dc, inputDispatchingTimeoutNanos, fullscreen, + showForAllUsers, targetSdk, orientation, rotationAnimationHint, configChanges, + launchTaskBehind, alwaysFocusable, controller); + } + int getWindowsCount() { return mChildren.size(); } @@ -237,6 +247,10 @@ class WindowTestsBase { WindowState getLastChild() { return mChildren.getLast(); } + + int positionInParent() { + return getParent().mChildren.indexOf(this); + } } /* Used so we can gain access to some protected members of the {@link Task} class */ @@ -337,6 +351,19 @@ class WindowTestsBase { mToken = token; } + @Override + AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token, + boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos, + boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation, + int rotationAnimationHint, int configChanges, boolean launchTaskBehind, + boolean alwaysFocusable, AppWindowContainerController controller) { + return new TestAppWindowToken(service, token, voiceInteraction, dc, + inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, + orientation, + rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable, + controller); + } + AppWindowToken getAppWindowToken() { return (AppWindowToken) sDisplayContent.getWindowToken(mToken.asBinder()); } |