diff options
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()); } |