diff options
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 { |