Modify StackBox and TaskStack methods.

Also add dump() throughout.

Change-Id: I5369d2e71262645d9b1015bd4e72fad395cc7547
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index f98d2d2..69ba3bb 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -1991,7 +1991,7 @@
                 group.tokens.add(r.appToken);
             }
         }
-        mService.mWindowManager.validateAppTokens(mValidateAppTokens);
+        mService.mWindowManager.validateAppTokens(mStackId, mValidateAppTokens);
     }
 
     /**
diff --git a/services/java/com/android/server/wm/DisplayContent.java b/services/java/com/android/server/wm/DisplayContent.java
index 0cd3136..bcefc41 100644
--- a/services/java/com/android/server/wm/DisplayContent.java
+++ b/services/java/com/android/server/wm/DisplayContent.java
@@ -80,7 +80,7 @@
      */
     final AppTokenList mExitingAppTokens = new AppTokenList();
 
-    ArrayList<StackBox> mStackBoxes = new ArrayList<StackBox>();
+    private ArrayList<StackBox> mStackBoxes = new ArrayList<StackBox>();
 
     /**
      * Sorted most recent at top, oldest at [0].
@@ -113,6 +113,11 @@
         return mDisplayInfo;
     }
 
+    /**
+     * Retrieve the tasks on this display in stack order from the topmost TaskStack down.
+     * Note that the order of TaskStacks in the same StackBox is defined within StackBox.
+     * @return All the Tasks, in order, on this display.
+     */
     ArrayList<Task> getTasks() {
         mTmpTasks.clear();
         int numBoxes = mStackBoxes.size();
@@ -126,6 +131,7 @@
         mDisplay.getDisplayInfo(mDisplayInfo);
     }
 
+    /** @return The number of tokens in all of the Tasks on this display. */
     int numTokens() {
         getTasks();
         int count = 0;
@@ -135,6 +141,7 @@
         return count;
     }
 
+    /** Refer to {@link WindowManagerService#createStack(int, int, int, float)} */
     TaskStack createStack(int stackId, int relativeStackId, int position, float weight) {
         TaskStack newStack = null;
         if (mStackBoxes.isEmpty()) {
@@ -150,8 +157,7 @@
                 if (position == StackBox.TASK_STACK_GOES_OVER
                         || position == StackBox.TASK_STACK_GOES_UNDER) {
                     // Position indicates a new box is added at top level only.
-                    final TaskStack stack = box.mStack;
-                    if (stack != null && stack.mStackId == relativeStackId) {
+                    if (box.contains(relativeStackId)) {
                         final int offset = position == StackBox.TASK_STACK_GOES_OVER ? 1 : 0;
                         StackBox newBox = new StackBox(this, box.mBounds);
                         newStack = new TaskStack(stackId, newBox);
@@ -167,13 +173,15 @@
                     }
                 }
             }
-            if (stackBoxNdx >= 0) {
-                throw new IllegalArgumentException("createStack: stackId " + stackId + " not found.");
+            if (stackBoxNdx < 0) {
+                throw new IllegalArgumentException("createStack: stackId " + relativeStackId
+                        + " not found.");
             }
         }
         return newStack;
     }
 
+    /** Refer to {@link WindowManagerService#resizeStack(int, float)} */
     boolean resizeStack(int stackId, float weight) {
         int stackBoxNdx;
         for (stackBoxNdx = mStackBoxes.size() - 1; stackBoxNdx >= 0; --stackBoxNdx) {
@@ -185,6 +193,24 @@
         return false;
     }
 
+    /**
+     * Reorder a StackBox within mStackBox. The StackBox to reorder is the one containing the
+     * specified TaskStack.
+     * @param stackId The TaskStack to reorder.
+     * @param toTop Move to the top of all StackBoxes if true, to the bottom if false. Only the
+     * topmost layer of StackBoxes, those in mStackBoxes can be reordered.
+     */
+    void moveStackBox(int stackId, boolean toTop) {
+        for (int stackBoxNdx = mStackBoxes.size() - 1; stackBoxNdx >= 0; --stackBoxNdx) {
+            final StackBox box = mStackBoxes.get(stackBoxNdx);
+            if (box.contains(stackId)) {
+                mStackBoxes.remove(box);
+                mStackBoxes.add(toTop ? mStackBoxes.size() : 0, box);
+                return;
+            }
+        }
+    }
+
     public void dump(String prefix, PrintWriter pw) {
         pw.print(prefix); pw.print("Display: mDisplayId="); pw.println(mDisplayId);
         final String subPrefix = "  " + prefix;
@@ -209,6 +235,10 @@
             pw.print("-"); pw.print(mDisplayInfo.largestNominalAppWidth);
             pw.print("x"); pw.println(mDisplayInfo.largestNominalAppHeight);
             pw.print(subPrefix); pw.print("layoutNeeded="); pw.println(layoutNeeded);
+            for (int boxNdx = 0; boxNdx < mStackBoxes.size(); ++boxNdx) {
+                pw.print(prefix); pw.print("StackBox #"); pw.println(boxNdx);
+                mStackBoxes.get(boxNdx).dump(prefix + "  ", pw);
+            }
             int ndx = numTokens();
             if (ndx > 0) {
                 pw.println();
diff --git a/services/java/com/android/server/wm/StackBox.java b/services/java/com/android/server/wm/StackBox.java
index bd007e5..4bdb406 100644
--- a/services/java/com/android/server/wm/StackBox.java
+++ b/services/java/com/android/server/wm/StackBox.java
@@ -18,6 +18,7 @@
 
 import android.graphics.Rect;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 
 public class StackBox {
@@ -29,15 +30,32 @@
     public static final int TASK_STACK_GOES_OVER = 4;
     public static final int TASK_STACK_GOES_UNDER = 5;
 
+    /** The display this box sits in. */
     final DisplayContent mDisplayContent;
+
+    /** Non-null indicates this is mFirst or mSecond of a parent StackBox. Null indicates this
+     * is this entire size of mDisplayContent. */
     StackBox mParent;
-    boolean mVertical;
+
+    /** First child, this is null exactly when mStack is non-null. */
     StackBox mFirst;
+
+    /** Second child, this is null exactly when mStack is non-null. */
     StackBox mSecond;
-    float mWeight;
+
+    /** Stack of Tasks, this is null exactly when mFirst and mSecond are non-null. */
     TaskStack mStack;
+
+    /** Content limits relative to the DisplayContent this sits in. */
     Rect mBounds;
+
+    /** Relative orientation of mFirst and mSecond. */
+    boolean mVertical;
+
+    /** Dirty flag. Something inside this or some descendant of this has changed. */
     boolean layoutNeeded;
+
+    /** Used to keep from reallocating a temporary array to hold the list of Tasks below */
     ArrayList<Task> mTmpTasks = new ArrayList<Task>();
 
     StackBox(DisplayContent displayContent, Rect bounds) {
@@ -62,6 +80,28 @@
         }
     }
 
+    /**
+     * Detremine if a particular TaskStack is in this StackBox or any of its descendants.
+     * @param stackId The TaskStack being considered.
+     * @return true if the specified TaskStack is in this box or its descendants. False otherwise.
+     */
+    boolean contains(int stackId) {
+        if (mStack != null) {
+            return mStack.mStackId == stackId;
+        }
+        return mFirst.contains(stackId) || mSecond.contains(stackId);
+    }
+
+    /**
+     * Create a new TaskStack relative to a specified one by splitting the StackBox containing
+     * the specified TaskStack into two children. The size and position each of the new StackBoxes
+     * is determined by the passed parameters.
+     * @param stackId The id of the new TaskStack to create.
+     * @param relativeStackId The id of the TaskStack to place the new one next to.
+     * @param position One of the static TASK_STACK_GOES_xxx positions defined in this class.
+     * @param weight The percentage size of the parent StackBox to devote to the new TaskStack.
+     * @return The new TaskStack.
+     */
     TaskStack split(int stackId, int relativeStackId, int position, float weight) {
         if (mStack != null) {
             if (mStack.mStackId == relativeStackId) {
@@ -112,6 +152,7 @@
                 mStack = null;
                 return stack;
             }
+            // Not the intended TaskStack.
             return null;
         }
 
@@ -123,51 +164,11 @@
         return mSecond.split(stackId, relativeStackId, position, weight);
     }
 
-    TaskStack merge(int position) {
-        TaskStack stack = null;
-        if (mFirst != null) {
-            switch (position) {
-                default:
-                case TASK_STACK_GOES_BEFORE:
-                case TASK_STACK_GOES_ABOVE:
-                    stack = mFirst.merge(position);
-                    stack.merge(mSecond.merge(position));
-                    break;
-                case TASK_STACK_GOES_AFTER:
-                case TASK_STACK_GOES_BELOW:
-                    stack = mSecond.merge(position);
-                    stack.merge(mFirst.merge(position));
-                    break;
-            }
-            return stack;
-        }
-        return mStack;
-    }
-
-    boolean merge(int stackId, int position, StackBox primary, StackBox secondary) {
-        TaskStack stack = primary.mStack;
-        if (stack != null && stack.mStackId == stackId) {
-            stack.merge(secondary.merge(position));
-            mStack = stack;
-            mFirst = null;
-            mSecond = null;
-            return true;
-        }
-        return false;
-    }
-
-    boolean merge(int stackId, int position) {
-        if (mFirst != null) {
-            if (merge(stackId, position, mFirst, mSecond)
-                    || merge(stackId, position, mSecond, mFirst)
-                    || mFirst.merge(stackId, position)
-                    || mSecond.merge(stackId, position)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
+    /**
+     * @return List of all Tasks underneath this StackBox. The order is currently mFirst followed
+     * by mSecond putting mSecond Tasks more recent than mFirst Tasks.
+     * TODO: Change to MRU ordering.
+     */
     ArrayList<Task> getTasks() {
         mTmpTasks.clear();
         if (mStack != null) {
@@ -179,37 +180,55 @@
         return mTmpTasks;
     }
 
-    void absorb(StackBox box) {
-        mFirst = box.mFirst;
-        mSecond = box.mSecond;
-        mStack = box.mStack;
+    /** Combine a child StackBox into its parent.
+     * @param child The surviving child to be merge up into this StackBox. */
+    void absorb(StackBox child) {
+        mFirst = child.mFirst;
+        mSecond = child.mSecond;
+        mStack = child.mStack;
         layoutNeeded = true;
     }
 
-    void removeStack() {
-        if (mParent != null) {
-            if (mParent.mFirst == this) {
-                mParent.absorb(mParent.mSecond);
-            } else {
-                mParent.absorb(mParent.mFirst);
-            }
-            mParent.makeDirty();
-        }
-    }
-
-    boolean addTaskToStack(Task task, int stackId, boolean toTop) {
+    /** Return the stackId of the first mFirst StackBox with a non-null mStack */
+    int getStackId() {
         if (mStack != null) {
-            if (mStack.mStackId == stackId) {
-                mStack.addTask(task, toTop);
-                return true;
-            }
-            return false;
+            return mStack.mStackId;
         }
-        return mFirst.addTaskToStack(task, stackId, toTop)
-                || mSecond.addTaskToStack(task, stackId, toTop);
+        return mFirst.getStackId();
     }
 
+    /** Remove this box and propagate its sibling's content up to their parent.
+     * @return The first stackId of the resulting StackBox. */
+    int removeStack() {
+        if (mParent.mFirst == this) {
+            mParent.absorb(mParent.mSecond);
+        } else {
+            mParent.absorb(mParent.mFirst);
+        }
+        mParent.makeDirty();
+        return getStackId();
+    }
+
+    /** TODO: */
     boolean resize(int stackId, float weight) {
         return false;
     }
+
+    public void dump(String prefix, PrintWriter pw) {
+        pw.print(prefix); pw.print("mParent="); pw.println(mParent);
+        pw.print(prefix); pw.print("mFirst="); pw.println(mFirst);
+        pw.print(prefix); pw.print("mSecond="); pw.println(mSecond);
+        pw.print(prefix); pw.print("mBounds="); pw.print(mBounds.toShortString());
+            pw.print("mVertical="); pw.print(mVertical);
+            pw.print("layoutNeeded="); pw.println(layoutNeeded);
+        if (mStack != null) {
+            pw.print(prefix); pw.print("mStack="); pw.println(mStack);
+            mStack.dump(prefix + "  ", pw);
+        } else {
+            pw.print(prefix); pw.print("mFirst="); pw.println(mStack);
+            mFirst.dump(prefix + "  ", pw);
+            pw.print(prefix); pw.print("mSecond="); pw.println(mStack);
+            mSecond.dump(prefix + "  ", pw);
+        }
+    }
 }
diff --git a/services/java/com/android/server/wm/TaskStack.java b/services/java/com/android/server/wm/TaskStack.java
index 4d34b36..753ef0d 100644
--- a/services/java/com/android/server/wm/TaskStack.java
+++ b/services/java/com/android/server/wm/TaskStack.java
@@ -16,14 +16,22 @@
 
 package com.android.server.wm;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 
 public class TaskStack {
+    /** Unique identifier */
     final int mStackId;
-    final DisplayContent mDisplayContent;
+
+    /** The display this stack sits under. */
+    private final DisplayContent mDisplayContent;
+
+    /** The Tasks that define this stack. Oldest Tasks are at the bottom. The ordering must match
+     * mTaskHistory in the ActivityStack with the same mStackId */
     private ArrayList<Task> mTasks = new ArrayList<Task>();
-    int mLayer;
-    StackBox mParent;
+
+    /** The StackBox this sits in. */
+    private final StackBox mParent;
 
     TaskStack(int stackId, StackBox parent) {
         mStackId = stackId;
@@ -46,9 +54,15 @@
         return taskLists;
     }
 
+    /**
+     * Put a Task in this stack. Used for adding and moving.
+     * @param task The task to add.
+     * @param toTop Whether to add it to the top or bottom.
+     */
     void addTask(Task task, boolean toTop) {
         mParent.makeDirty();
         mTasks.add(toTop ? mTasks.size() : 0, task);
+        mDisplayContent.moveStackBox(mStackId, toTop);
     }
 
     void moveTaskToTop(Task task) {
@@ -61,6 +75,12 @@
         addTask(task, false);
     }
 
+    /**
+     * Delete a Task from this stack. If it is the last Task in the stack, remove this stack from
+     * its parent StackBox and merge the parent.
+     * @param task The Task to delete.
+     * @return True if #task was in this stack.
+     */
     boolean removeTask(Task task) {
         mParent.makeDirty();
         if (mTasks.remove(task)) {
@@ -72,6 +92,10 @@
         return false;
     }
 
+    int remove() {
+        return mParent.removeStack();
+    }
+
     int numTokens() {
         int count = 0;
         for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
@@ -79,4 +103,11 @@
         }
         return count;
     }
+
+    public void dump(String prefix, PrintWriter pw) {
+        pw.print(prefix); pw.print("mStackId="); pw.println(mStackId);
+        for (int taskNdx = 0; taskNdx < mTasks.size(); ++taskNdx) {
+            pw.print(prefix); pw.println(mTasks.get(taskNdx));
+        }
+    }
 }
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index d586f7b..0b42a2d 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -3169,7 +3169,7 @@
     // Application Window Tokens
     // -------------------------------------------------------------
 
-    public void validateAppTokens(List<TaskGroup> tasks) {
+    public void validateAppTokens(int stackId, List<TaskGroup> tasks) {
         synchronized (mWindowMap) {
             int t = tasks.size() - 1;
             if (t < 0) {
@@ -3186,7 +3186,7 @@
                 return;
             }
 
-            final ArrayList<Task> localTasks = displayContent.getTasks();
+            final ArrayList<Task> localTasks = mStackIdToStack.get(stackId).getTasks();
             int taskNdx;
             for (taskNdx = localTasks.size() - 1; taskNdx >= 0 && t >= 0; --taskNdx, --t) {
                 AppTokenList localTokens = localTasks.get(taskNdx).mAppTokens;
@@ -3230,6 +3230,10 @@
         }
     }
 
+    public void validateStackOrder(Integer[] remoteStackIds) {
+        // TODO:
+    }
+
     boolean checkCallingPermission(String permission, String func) {
         // Quick check: if the calling permission is me, it's all okay.
         if (Binder.getCallingPid() == Process.myPid()) {
@@ -4685,10 +4689,18 @@
             TaskStack stack = getDefaultDisplayContentLocked().createStack(stackId,
                     relativeStackId, position, weight);
             mStackIdToStack.put(stackId, stack);
-            performLayoutAndPlaceSurfacesLocked();
         }
     }
 
+    public int removeStack(int stackId) {
+        final TaskStack stack = mStackIdToStack.get(stackId);
+        if (stack != null) {
+            mStackIdToStack.delete(stackId);
+            return stack.remove();
+        }
+        return -1;
+    }
+
     public void moveTaskToStack(int taskId, int stackId, boolean toTop) {
         synchronized (mWindowMap) {
             Task task = mTaskIdToTask.get(taskId);