summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/IActivityManager.aidl5
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java5
-rw-r--r--services/core/java/com/android/server/am/ActivityStack.java5
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java9
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java46
-rw-r--r--services/core/java/com/android/server/wm/Task.java24
-rw-r--r--services/core/java/com/android/server/wm/TaskStack.java102
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java64
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java24
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java69
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java59
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java112
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java23
13 files changed, 455 insertions, 92 deletions
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index fcc6e3d1e913..28ad01e8178d 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -453,6 +453,11 @@ interface IActivityManager {
// Stop Binder transaction tracking for all applications and dump trace data to the given file
// descriptor.
boolean stopBinderTrackingAndDump(in ParcelFileDescriptor fd);
+ /**
+ * Try to place task to provided position. The final position might be different depending on
+ * current user and stacks state. The task will be moved to target stack if it's currently in
+ * different stack.
+ */
void positionTaskInStack(int taskId, int stackId, int position);
int getActivityStackId(in IBinder token);
void exitFreeformMode(in IBinder token);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 19dcf41f542c..1583023b48e1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -10044,6 +10044,11 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
+ /**
+ * Try to place task to provided position. The final position might be different depending on
+ * current user and stacks state. The task will be moved to target stack if it's currently in
+ * different stack.
+ */
@Override
public void positionTaskInStack(int taskId, int stackId, int position) {
enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "positionTaskInStack()");
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 5bdae574421b..ba95abd10f36 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -2547,6 +2547,10 @@ final class ActivityStack extends ConfigurationContainer {
return null;
}
+ /**
+ * Used from {@link ActivityStack#positionTask(TaskRecord, int)}.
+ * @see ActivityManagerService#positionTaskInStack(int, int, int).
+ */
private void insertTaskAtPosition(TaskRecord task, int position) {
if (position >= mTaskHistory.size()) {
insertTaskAtTop(task, null);
@@ -4935,6 +4939,7 @@ final class ActivityStack extends ConfigurationContainer {
postAddTask(task, prevStack);
}
+ /** @see ActivityManagerService#positionTaskInStack(int, int, int). */
void positionTask(final TaskRecord task, int position) {
final ActivityRecord topRunningActivity = task.topRunningActivityLocked();
final boolean wasResumed = topRunningActivity == task.getStack().mResumedActivity;
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index b4b346576507..235325b8b259 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2200,7 +2200,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer
} else {
for (int i = size - 1; i >= 0; i--) {
positionTaskInStackLocked(tasks.get(i).taskId,
- FULLSCREEN_WORKSPACE_STACK_ID, 0);
+ FULLSCREEN_WORKSPACE_STACK_ID, 0 /* position */);
}
}
} finally {
@@ -2872,6 +2872,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer
return true;
}
+ /** @see ActivityManagerService#positionTaskInStack(int, int, int). */
void positionTaskInStackLocked(int taskId, int stackId, int position) {
final TaskRecord task = anyTaskForIdLocked(taskId);
if (task == null) {
@@ -2882,6 +2883,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer
task.updateOverrideConfigurationForStack(stack);
+ // TODO: Return final position from WM for AM to use instead of duplicating computations in
+ // ActivityStack#insertTaskAtPosition.
mWindowManager.positionTaskInStack(
taskId, stackId, position, task.mBounds, task.getOverrideConfiguration());
stack.positionTask(task, position);
@@ -4322,7 +4325,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer
if (activityDisplay == null) {
return;
}
- addToDisplayLocked(activityDisplay, true);
+ addToDisplayLocked(activityDisplay, true /* onTop */);
}
}
@@ -4538,7 +4541,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer
new VirtualActivityDisplay(width, height, density);
mActivityDisplay = virtualActivityDisplay;
mActivityDisplays.put(virtualActivityDisplay.mDisplayId, virtualActivityDisplay);
- addToDisplayLocked(virtualActivityDisplay, true);
+ addToDisplayLocked(virtualActivityDisplay, true /* onTop */);
}
if (mSurface != null) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 203137d4d481..135a3dc9f02a 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -979,7 +979,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
// It's already attached to the display...clear mDeferRemoval and move stack to
// appropriate z-order on display as needed.
stack.mDeferRemoval = false;
- moveStack(stack, onTop);
+ // We're not moving the display to front when we're adding stacks, only when
+ // requested to change the position of stack explicitly.
+ mTaskStackContainers.positionChildAt(onTop ? POSITION_TOP : POSITION_BOTTOM, stack,
+ false /* includingParents */);
attachedToDisplay = true;
} else {
stack = new TaskStack(mService, stackId);
@@ -1031,10 +1034,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
return addStackToDisplay(stack.mStackId, true /* onTop */);
}
- void moveStack(TaskStack stack, boolean toTop) {
- mTaskStackContainers.moveStack(stack, toTop);
- }
-
@Override
protected void addChild(DisplayChildWindowContainer child,
Comparator<DisplayChildWindowContainer> comparator) {
@@ -1057,6 +1056,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
throw new UnsupportedOperationException("See DisplayChildWindowContainer");
}
+ @Override
+ void positionChildAt(int position, DisplayChildWindowContainer child, boolean includingParents) {
+ // Children of the display are statically ordered, so the real intention here is to perform
+ // the operation on the display and not the static direct children.
+ getParent().positionChildAt(position, this, includingParents);
+ }
+
int taskIdFromPoint(int x, int y) {
for (int stackNdx = mTaskStackContainers.size() - 1; stackNdx >= 0; --stackNdx) {
final TaskStack stack = mTaskStackContainers.get(stackNdx);
@@ -2499,20 +2505,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
stack.onRemovedFromDisplay();
}
- void moveStack(TaskStack stack, boolean toTop) {
- if (StackId.isAlwaysOnTop(stack.mStackId) && !toTop) {
- // This stack is always-on-top silly...
- Slog.w(TAG_WM, "Ignoring move of always-on-top stack=" + stack + " to bottom");
- return;
- }
-
- if (!mChildren.contains(stack)) {
- Slog.wtf(TAG_WM, "moving stack that was not added: " + stack, new Throwable());
- }
- removeChild(stack);
- addChild(stack, toTop);
- }
-
private void addChild(TaskStack stack, boolean toTop) {
int addIndex = toTop ? mChildren.size() : 0;
@@ -2532,6 +2524,22 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
@Override
+ void positionChildAt(int position, TaskStack child, boolean includingParents) {
+ if (StackId.isAlwaysOnTop(child.mStackId) && position != POSITION_TOP) {
+ // This stack is always-on-top, override the default behavior.
+ Slog.w(TAG_WM, "Ignoring move of always-on-top stack=" + this + " to bottom");
+
+ // Moving to its current position, as we must call super but we don't want to
+ // perform any meaningful action.
+ final int currentPosition = mChildren.indexOf(child);
+ super.positionChildAt(currentPosition, child, false /* includingParents */);
+ return;
+ }
+
+ super.positionChildAt(position, child, includingParents);
+ }
+
+ @Override
boolean forAllWindows(ToBooleanFunction<WindowState> callback,
boolean traverseTopToBottom) {
if (traverseTopToBottom) {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index aa6e3c7c3e45..6005a991e474 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -23,6 +23,7 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSC
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.H.RESIZE_TASK;
@@ -154,7 +155,7 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU
mService.mTaskIdToTask.delete(mTaskId);
}
- // Change to use re-parenting in WC when TaskStack is switched to use WC.
+ // TODO: Change to use re-parenting in WC.
void moveTaskToStack(TaskStack stack, boolean toTop) {
if (stack == mStack) {
return;
@@ -166,15 +167,20 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU
stack.addTask(this, toTop);
}
+ /** @see com.android.server.am.ActivityManagerService#positionTaskInStack(int, int, int). */
void positionTaskInStack(TaskStack stack, int position, Rect bounds,
Configuration overrideConfig) {
if (mStack != null && stack != mStack) {
+ // Task is already attached to a different stack. First we need to remove it from there
+ // and add to top of the target stack. We will move it proper position afterwards.
if (DEBUG_STACK) Slog.i(TAG, "positionTaskInStack: removing taskId=" + mTaskId
+ " from stack=" + mStack);
EventLog.writeEvent(EventLogTags.WM_TASK_REMOVED, mTaskId, "moveTask");
mStack.removeChild(this);
+ stack.addTask(this, true /* toTop */);
}
- stack.positionTask(this, position, showForAllUsers());
+
+ stack.positionChildAt(position, this, true /* includingParents */);
resizeLocked(bounds, overrideConfig, false /* force */);
for (int activityNdx = mChildren.size() - 1; activityNdx >= 0; --activityNdx) {
@@ -183,6 +189,20 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU
}
@Override
+ void onParentSet() {
+ // Update task bounds if needed.
+ updateDisplayInfo(getDisplayContent());
+
+ if (StackId.windowsAreScaleable(mStack.mStackId)) {
+ // We force windows out of SCALING_MODE_FREEZE so that we can continue to animate them
+ // while a resize is pending.
+ forceWindowsScaleable(true /* force */);
+ } else {
+ forceWindowsScaleable(false /* force */);
+ }
+ }
+
+ @Override
void removeChild(AppWindowToken token) {
if (!mChildren.contains(token)) {
Slog.e(TAG, "removeChild: token=" + this + " not found.");
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 74c0919699fc..d7c7cfa319e1 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -512,27 +512,67 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
}
/**
- * Put a Task in this stack. Used for adding and moving.
+ * Put a Task in this stack. Used for adding only.
+ * When task is added to top of the stack, the entire branch of the hierarchy (including stack
+ * and display) will be brought to top.
* @param task The task to add.
* @param toTop Whether to add it to the top or bottom.
* @param showForAllUsers Whether to show the task regardless of the current user.
*/
void addTask(Task task, boolean toTop, boolean showForAllUsers) {
- positionTask(task, toTop ? mChildren.size() : 0, showForAllUsers);
+ final TaskStack currentStack = task.mStack;
+ // TODO: We pass stack to task's constructor, but we still need to call this method.
+ // This doesn't make sense, mStack will already be set equal to "this" at this point.
+ if (currentStack != null && currentStack.mStackId != mStackId) {
+ throw new IllegalStateException("Trying to add taskId=" + task.mTaskId
+ + " to stackId=" + mStackId
+ + ", but it is already attached to stackId=" + task.mStack.mStackId);
+ }
+
+ final int targetPosition = toTop ? mChildren.size() : 0;
+
+ // Add child task.
+ task.mStack = this;
+ addChild(task, targetPosition);
+
+ // Move child to a proper position, as some restriction for position might apply.
+ positionChildAt(targetPosition, task, true /* includingParents */, showForAllUsers);
+ }
+
+ @Override
+ void positionChildAt(int position, Task child, boolean includingParents) {
+ positionChildAt(position, child, includingParents, child.showForAllUsers());
+ }
+
+ /**
+ * Overridden version of {@link TaskStack#positionChildAt(int, Task, boolean)}. Used in
+ * {@link TaskStack#addTask(Task, boolean, boolean showForAllUsers)}, as it can receive
+ * showForAllUsers param from {@link AppWindowToken} instead of {@link Task#showForAllUsers()}.
+ */
+ private void positionChildAt(int position, Task child, boolean includingParents,
+ boolean showForAllUsers) {
+ final int targetPosition = findPositionForTask(child, position, showForAllUsers,
+ false /* addingNew */);
+ super.positionChildAt(targetPosition, child, includingParents);
+
+ // Log positioning.
+ if (DEBUG_TASK_MOVEMENT)
+ Slog.d(TAG_WM, "positionTask: task=" + this + " position=" + position);
+
+ final int toTop = targetPosition == mChildren.size() - 1 ? 1 : 0;
+ EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, child.mTaskId, toTop, targetPosition);
}
// TODO: We should really have users as a window container in the hierarchy so that we don't
- // have to do complicated things like we are doing in this method and also also the method to
- // just be WindowContainer#addChild
- void positionTask(Task task, int position, boolean showForAllUsers) {
+ // have to do complicated things like we are doing in this method.
+ private int findPositionForTask(Task task, int targetPosition, boolean showForAllUsers,
+ boolean addingNew) {
final boolean canShowTask =
showForAllUsers || mService.isCurrentProfileLocked(task.mUserId);
- if (mChildren.contains(task)) {
- super.removeChild(task);
- }
- int stackSize = mChildren.size();
+
+ final int stackSize = mChildren.size();
int minPosition = 0;
- int maxPosition = stackSize;
+ int maxPosition = addingNew ? stackSize : stackSize - 1;
if (canShowTask) {
minPosition = computeMinPosition(minPosition, stackSize);
@@ -540,28 +580,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
maxPosition = computeMaxPosition(maxPosition);
}
// Reset position based on minimum/maximum possible positions.
- position = Math.min(Math.max(position, minPosition), maxPosition);
-
- if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM,
- "positionTask: task=" + task + " position=" + position);
- addChild(task, position);
- task.mStack = this;
- task.updateDisplayInfo(mDisplayContent);
- boolean toTop = position == mChildren.size() - 1;
- if (toTop) {
- // TODO: Have a WidnowContainer method that moves all parents of a container to the
- // front for cases like this.
- mDisplayContent.moveStack(this, true);
- }
-
- if (StackId.windowsAreScaleable(mStackId)) {
- // We force windows out of SCALING_MODE_FREEZE so that we can continue to animate them
- // while a resize is pending.
- task.forceWindowsScaleable(true);
- } else {
- task.forceWindowsScaleable(false);
- }
- EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, task.mTaskId, toTop ? 1 : 0, position);
+ return Math.min(Math.max(targetPosition, minPosition), maxPosition);
}
/** Calculate the minimum possible position for a task that can be shown to the user.
@@ -591,7 +610,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
*/
private int computeMaxPosition(int maxPosition) {
while (maxPosition > 0) {
- final Task tmpTask = mChildren.get(maxPosition - 1);
+ final Task tmpTask = mChildren.get(maxPosition);
final boolean canShowTmpTask =
tmpTask.showForAllUsers()
|| mService.isCurrentProfileLocked(tmpTask.mUserId);
@@ -603,20 +622,6 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
return maxPosition;
}
- // TODO: Have functionality in WC to move things to the bottom or top. Also, look at the call
- // points for this methods to see if we need functionality to move something to the front/bottom
- // with its parents.
- void moveTaskToTop(Task task) {
- if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM, "moveTaskToTop: task=" + task + " Callers="
- + Debug.getCallers(6));
- addTask(task, true);
- }
-
- void moveTaskToBottom(Task task) {
- if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM, "moveTaskToBottom: task=" + task);
- addTask(task, false);
- }
-
/**
* Delete a Task from this stack. If it is the last Task in the stack, move this stack to the
* back.
@@ -627,10 +632,11 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye
if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM, "removeChild: task=" + task);
super.removeChild(task);
+ task.mStack = null;
if (mDisplayContent != null) {
if (mChildren.isEmpty()) {
- mDisplayContent.moveStack(this, false);
+ getParent().positionChildAt(POSITION_BOTTOM, this, false /* includingParents */);
}
mDisplayContent.setLayoutNeeded();
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 4a1c0678a661..984cf55de0e7 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -39,6 +39,9 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
*/
class WindowContainer<E extends WindowContainer> implements Comparable<WindowContainer> {
+ static final int POSITION_TOP = Integer.MAX_VALUE;
+ static final int POSITION_BOTTOM = Integer.MIN_VALUE;
+
/**
* The parent of this window container.
* For removing or setting new parent {@link #setParent} should be used, because it also
@@ -86,6 +89,16 @@ class WindowContainer<E extends WindowContainer> implements Comparable<WindowCon
// Update merged override configuration of this container and all its children.
onMergedOverrideConfigurationChanged();
}
+
+ onParentSet();
+ }
+
+ /**
+ * Callback that is triggered when @link WindowContainer#setParent(WindowContainer)} was called.
+ * Supposed to be overridden and contain actions that should be executed after parent was set.
+ */
+ void onParentSet() {
+ // Do nothing by default.
}
// Temp. holders for a chain of containers we are currently processing.
@@ -203,6 +216,57 @@ class WindowContainer<E extends WindowContainer> implements Comparable<WindowCon
}
/**
+ * Move a child from it's current place in siblings list to the specified position,
+ * with an option to move all its parents to top.
+ * @param position Target position to move the child to.
+ * @param child Child to move to selected position.
+ * @param includingParents Flag indicating whether we need to move the entire branch of the
+ * hierarchy when we're moving a child to {@link #POSITION_TOP} or
+ * {@link #POSITION_BOTTOM}. When moving to other intermediate positions
+ * this flag will do nothing.
+ */
+ @CallSuper
+ void positionChildAt(int position, E child, boolean includingParents) {
+ if ((position < 0 && position != POSITION_BOTTOM)
+ || (position >= mChildren.size() && position != POSITION_TOP)) {
+ throw new IllegalArgumentException("positionAt: invalid position=" + position
+ + ", children number=" + mChildren.size());
+ }
+
+ if (position == mChildren.size() - 1) {
+ position = POSITION_TOP;
+ } else if (position == 0) {
+ position = POSITION_BOTTOM;
+ }
+
+ switch (position) {
+ case POSITION_TOP:
+ if (mChildren.getLast() != child) {
+ mChildren.remove(child);
+ mChildren.addLast(child);
+ }
+ if (includingParents && getParent() != null) {
+ getParent().positionChildAt(POSITION_TOP, this /* child */,
+ true /* includingParents */);
+ }
+ break;
+ case POSITION_BOTTOM:
+ if (mChildren.getFirst() != child) {
+ mChildren.remove(child);
+ mChildren.addFirst(child);
+ }
+ if (includingParents && getParent() != null) {
+ getParent().positionChildAt(POSITION_BOTTOM, this /* child */,
+ true /* includingParents */);
+ }
+ break;
+ default:
+ mChildren.remove(child);
+ mChildren.add(position, child);
+ }
+ }
+
+ /**
* Returns full configuration applied to this window container.
* This method should be used for getting settings applied in each particular level of the
* hierarchy.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 577e5a0c2e20..0f1675098c18 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -209,6 +209,8 @@ import static com.android.server.wm.AppWindowAnimator.PROLONG_ANIMATION_AT_END;
import static com.android.server.wm.AppWindowAnimator.PROLONG_ANIMATION_AT_START;
import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
+import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
@@ -3311,26 +3313,17 @@ public class WindowManagerService extends IWindowManager.Stub
final long origId = Binder.clearCallingIdentity();
try {
synchronized(mWindowMap) {
- Task task = mTaskIdToTask.get(taskId);
+ final Task task = mTaskIdToTask.get(taskId);
if (task == null) {
// Normal behavior, addAppToken will be called next and task will be created.
return;
}
- final TaskStack stack = task.mStack;
- final DisplayContent displayContent = task.getDisplayContent();
- displayContent.moveStack(stack, true);
- if (displayContent.isDefaultDisplay) {
- final TaskStack homeStack = displayContent.getHomeStack();
- if (homeStack != stack) {
- // When a non-home stack moves to the top, the home stack moves to the
- // bottom.
- displayContent.moveStack(homeStack, false);
- }
- }
- stack.moveTaskToTop(task);
+ task.mStack.positionChildAt(POSITION_TOP, task, true /* includingParents */);
+
if (mAppTransition.isTransitionSet()) {
task.setSendingToBottom(false);
}
+ final DisplayContent displayContent = task.getDisplayContent();
displayContent.layoutAndAssignWindowLayersIfNeeded();
}
} finally {
@@ -3342,14 +3335,14 @@ public class WindowManagerService extends IWindowManager.Stub
final long origId = Binder.clearCallingIdentity();
try {
synchronized(mWindowMap) {
- Task task = mTaskIdToTask.get(taskId);
+ final Task task = mTaskIdToTask.get(taskId);
if (task == null) {
Slog.e(TAG_WM, "moveTaskToBottom: taskId=" + taskId
+ " not found in mTaskIdToTask");
return;
}
final TaskStack stack = task.mStack;
- stack.moveTaskToBottom(task);
+ stack.positionChildAt(POSITION_BOTTOM, task, false /* includingParents */);
if (mAppTransition.isTransitionSet()) {
task.setSendingToBottom(true);
}
@@ -3656,6 +3649,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ /** @see com.android.server.am.ActivityManagerService#positionTaskInStack(int, int, int). */
public void positionTaskInStack(int taskId, int stackId, int position, Rect bounds,
Configuration overrideConfig) {
synchronized (mWindowMap) {
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java
new file mode 100644
index 000000000000..746310217c38
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests for the {@link DisplayContent.TaskStackContainers} container in {@link DisplayContent}.
+ *
+ * Build/Install/Run:
+ * bit FrameworksServicesTests:com.android.server.wm.TaskStackContainersTests
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class TaskStackContainersTests extends WindowTestsBase {
+
+ @Test
+ public void testStackPositionChildAt() throws Exception {
+ // Test that always-on-top stack can't be moved to position other than top.
+ final TaskStack stack1 = createTaskStackOnDisplay(sDisplayContent);
+ final TaskStack stack2 = createTaskStackOnDisplay(sDisplayContent);
+ sDisplayContent.addStackToDisplay(PINNED_STACK_ID, true);
+ final TaskStack pinnedStack = sWm.mStackIdToStack.get(PINNED_STACK_ID);
+
+ final WindowContainer taskStackContainer = stack1.getParent();
+
+ final int stack1Pos = taskStackContainer.mChildren.indexOf(stack1);
+ final int stack2Pos = taskStackContainer.mChildren.indexOf(stack2);
+ final int pinnedStackPos = taskStackContainer.mChildren.indexOf(pinnedStack);
+
+ taskStackContainer.positionChildAt(WindowContainer.POSITION_BOTTOM, pinnedStack, false);
+ assertEquals(taskStackContainer.mChildren.get(stack1Pos), stack1);
+ assertEquals(taskStackContainer.mChildren.get(stack2Pos), stack2);
+ assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), pinnedStack);
+
+ taskStackContainer.positionChildAt(1, pinnedStack, false);
+ assertEquals(taskStackContainer.mChildren.get(stack1Pos), stack1);
+ assertEquals(taskStackContainer.mChildren.get(stack2Pos), stack2);
+ assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), pinnedStack);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java
new file mode 100644
index 000000000000..eca2500642fe
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests for the {@link TaskStack} class.
+ *
+ * Build/Install/Run:
+ * bit FrameworksServicesTests:com.android.server.wm.TaskStackTests
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class TaskStackTests extends WindowTestsBase {
+
+ @Test
+ public void testStackPositionChildAt() throws Exception {
+ final TaskStack stack = createTaskStackOnDisplay(sDisplayContent);
+ final Task task1 = createTaskInStack(stack, 0 /* userId */);
+ final Task task2 = createTaskInStack(stack, 1 /* userId */);
+
+ // Current user task should be moved to top.
+ stack.positionChildAt(WindowContainer.POSITION_TOP, task1, false /* includingParents */);
+ assertEquals(stack.mChildren.get(0), task2);
+ assertEquals(stack.mChildren.get(1), task1);
+
+ // Non-current user won't be moved to top.
+ stack.positionChildAt(WindowContainer.POSITION_TOP, task2, false /* includingParents */);
+ assertEquals(stack.mChildren.get(0), task2);
+ assertEquals(stack.mChildren.get(1), task1);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
index 0ccd0ada8ac8..7277ba42c2da 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
@@ -33,6 +33,10 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCA
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+
+import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -87,6 +91,14 @@ public class WindowContainerTests {
assertEquals(layer1, root.getChildAt(4));
assertEquals(secondLayer1, root.getChildAt(5));
assertEquals(layer2, root.getChildAt(6));
+
+ assertTrue(layer1.mOnParentSetCalled);
+ assertTrue(secondLayer1.mOnParentSetCalled);
+ assertTrue(layer2.mOnParentSetCalled);
+ assertTrue(layerNeg1.mOnParentSetCalled);
+ assertTrue(layerNeg2.mOnParentSetCalled);
+ assertTrue(secondLayerNeg1.mOnParentSetCalled);
+ assertTrue(layer0.mOnParentSetCalled);
}
@Test
@@ -180,6 +192,99 @@ public class WindowContainerTests {
}
@Test
+ public void testPositionChildAt() throws Exception {
+ final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+ final TestWindowContainer root = builder.setLayer(0).build();
+
+ final TestWindowContainer child1 = root.addChildWindow();
+ final TestWindowContainer child2 = root.addChildWindow();
+ final TestWindowContainer child3 = root.addChildWindow();
+
+ // Test position at top.
+ root.positionChildAt(POSITION_TOP, child1, false /* includingParents */);
+ assertEquals(child1, root.getChildAt(root.getChildrenCount() - 1));
+
+ // Test position at bottom.
+ root.positionChildAt(POSITION_BOTTOM, child1, false /* includingParents */);
+ assertEquals(child1, root.getChildAt(0));
+
+ // Test position in the middle.
+ root.positionChildAt(1, child3, false /* includingParents */);
+ assertEquals(child1, root.getChildAt(0));
+ assertEquals(child3, root.getChildAt(1));
+ assertEquals(child2, root.getChildAt(2));
+ }
+
+ @Test
+ public void testPositionChildAtIncludeParents() throws Exception {
+ final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+ final TestWindowContainer root = builder.setLayer(0).build();
+
+ final TestWindowContainer child1 = root.addChildWindow();
+ final TestWindowContainer child2 = root.addChildWindow();
+ final TestWindowContainer child11 = child1.addChildWindow();
+ final TestWindowContainer child12 = child1.addChildWindow();
+ final TestWindowContainer child13 = child1.addChildWindow();
+ final TestWindowContainer child21 = child2.addChildWindow();
+ final TestWindowContainer child22 = child2.addChildWindow();
+ final TestWindowContainer child23 = child2.addChildWindow();
+
+ // Test moving to top.
+ child1.positionChildAt(POSITION_TOP, child11, true /* includingParents */);
+ assertEquals(child12, child1.getChildAt(0));
+ assertEquals(child13, child1.getChildAt(1));
+ assertEquals(child11, child1.getChildAt(2));
+ assertEquals(child2, root.getChildAt(0));
+ assertEquals(child1, root.getChildAt(1));
+
+ // Test moving to bottom.
+ child1.positionChildAt(POSITION_BOTTOM, child11, true /* includingParents */);
+ assertEquals(child11, child1.getChildAt(0));
+ assertEquals(child12, child1.getChildAt(1));
+ assertEquals(child13, child1.getChildAt(2));
+ assertEquals(child1, root.getChildAt(0));
+ assertEquals(child2, root.getChildAt(1));
+
+ // Test moving to middle, includeParents shouldn't do anything.
+ child2.positionChildAt(1, child21, true /* includingParents */);
+ assertEquals(child11, child1.getChildAt(0));
+ assertEquals(child12, child1.getChildAt(1));
+ assertEquals(child13, child1.getChildAt(2));
+ assertEquals(child22, child2.getChildAt(0));
+ assertEquals(child21, child2.getChildAt(1));
+ assertEquals(child23, child2.getChildAt(2));
+ assertEquals(child1, root.getChildAt(0));
+ assertEquals(child2, root.getChildAt(1));
+ }
+
+ @Test
+ public void testPositionChildAtInvalid() throws Exception {
+ final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+ final TestWindowContainer root = builder.setLayer(0).build();
+
+ final TestWindowContainer child1 = root.addChildWindow();
+ final TestWindowContainer child2 = root.addChildWindow();
+
+ boolean gotException = false;
+ try {
+ // Check response to negative position.
+ root.positionChildAt(-1, child1, false /* includingParents */);
+ } catch (IllegalArgumentException e) {
+ gotException = true;
+ }
+ assertTrue(gotException);
+
+ gotException = false;
+ try {
+ // Check response to position that's bigger than child number.
+ root.positionChildAt(2, child1, false /* includingParents */);
+ } catch (IllegalArgumentException e) {
+ gotException = true;
+ }
+ assertTrue(gotException);
+ }
+
+ @Test
public void testIsAnimating() throws Exception {
final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
final TestWindowContainer root = builder.setLayer(0).build();
@@ -548,6 +653,8 @@ public class WindowContainerTests {
private boolean mIsVisible;
private boolean mFillsParent;
+ private boolean mOnParentSetCalled;
+
/**
* Compares 2 window layers and returns -1 if the first is lesser than the second in terms
* of z-order and 1 otherwise.
@@ -598,6 +705,11 @@ public class WindowContainerTests {
}
@Override
+ void onParentSet() {
+ mOnParentSetCalled = true;
+ }
+
+ @Override
boolean isAnimating() {
return mIsAnimating || super.isAnimating();
}
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 41bf646a6a1d..398568771cd7 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -75,6 +75,7 @@ public class WindowTestsBase {
sLayersController = new WindowLayersController(sWm);
sDisplayContent = new DisplayContent(context.getDisplay(), sWm, sLayersController,
new WallpaperController(sWm));
+ sWm.mRoot.addChild(sDisplayContent, 0);
// Set-up some common windows.
sWallpaperWindow = createWindow(null, TYPE_WALLPAPER, sDisplayContent, "wallpaperWindow");
@@ -103,11 +104,8 @@ public class WindowTestsBase {
return new TestWindowToken(type, dc);
}
- final int stackId = sNextStackId++;
- dc.addStackToDisplay(stackId, true);
- final TaskStack stack = sWm.mStackIdToStack.get(stackId);
- final Task task = new Task(sNextTaskId++, stack, 0, sWm, null, EMPTY, false, 0, false);
- stack.addTask(task, true);
+ final TaskStack stack = createTaskStackOnDisplay(dc);
+ final Task task = createTaskInStack(stack, 0 /* userId */);
final TestAppWindowToken token = new TestAppWindowToken(dc);
task.addChild(token, 0);
return token;
@@ -136,6 +134,21 @@ public class WindowTestsBase {
return w;
}
+ /** Creates a {@link TaskStack} and adds it to the specified {@link DisplayContent}. */
+ TaskStack createTaskStackOnDisplay(DisplayContent dc) {
+ final int stackId = sNextStackId++;
+ dc.addStackToDisplay(stackId, true);
+ return sWm.mStackIdToStack.get(stackId);
+ }
+
+ /**Creates a {@link Task} and adds it to the specified {@link TaskStack}. */
+ Task createTaskInStack(TaskStack stack, int userId) {
+ final Task newTask = new Task(sNextTaskId++, stack, userId, sWm, null, EMPTY, false, 0,
+ false);
+ stack.addTask(newTask, true);
+ return newTask;
+ }
+
/* Used so we can gain access to some protected members of the {@link WindowToken} class */
class TestWindowToken extends WindowToken {