summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/am/ActivityRecord.java21
-rw-r--r--services/core/java/com/android/server/am/ActivityStack.java12
-rw-r--r--services/core/java/com/android/server/am/TaskRecord.java6
-rw-r--r--services/core/java/com/android/server/wm/AppWindowContainerController.java34
-rw-r--r--services/core/java/com/android/server/wm/AppWindowToken.java21
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java46
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java27
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());
}