Add AppWindowTokens to TaskList.

- Add/remove/move TaskLists from ActivityStack.
- Further isolate mHistory.
- Cleanup warnings by parameterizing ArrayList.
- Fix previous bugs.

Change-Id: Ife8c7b7347479c70f10467cc384283456149ac50
diff --git a/services/java/com/android/server/wm/DisplayContent.java b/services/java/com/android/server/wm/DisplayContent.java
index 906ea57..43a5f0c 100644
--- a/services/java/com/android/server/wm/DisplayContent.java
+++ b/services/java/com/android/server/wm/DisplayContent.java
@@ -16,12 +16,16 @@
 
 package com.android.server.wm;
 
+import android.os.Debug;
+import android.util.Slog;
 import android.util.SparseArray;
 import android.view.Display;
 import android.view.DisplayInfo;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
 
 class DisplayContentList extends ArrayList<DisplayContent> {
 }
@@ -34,7 +38,7 @@
  * WindowManagerService.mWindowMap.
  */
 class DisplayContent {
-//    private final static String TAG = "DisplayContent";
+    private final static String TAG = "DisplayContent";
 
     /** Unique identifier of this stack. */
     private final int mDisplayId;
@@ -97,7 +101,7 @@
     /**
      * Sorted most recent at top, oldest at [0].
      */
-//    ArrayList<TaskList> mTaskLists = new ArrayList<TaskList>();
+    ArrayList<TaskList> mTaskLists = new ArrayList<TaskList>();
     SparseArray<TaskList> mTaskIdToTaskList = new SparseArray<TaskList>();
 
     /**
@@ -133,24 +137,28 @@
     /**
      *  Find the location to insert a new AppWindowToken into the window-ordered app token list.
      * @param addPos The location the token was inserted into in mAppTokens.
-     * @param atoken The token to insert.
+     * @param wtoken The token to insert.
      */
-    void addAppToken(final int addPos, final AppWindowToken atoken) {
-        mAppTokens.add(addPos, atoken);
+    void addAppToken(final int addPos, final AppWindowToken wtoken) {
+        mAppTokens.add(addPos, wtoken);
 
         if (addPos == 0 || addPos == mAnimatingAppTokens.size()) {
             // It was inserted into the beginning or end of mAppTokens. Honor that.
-            mAnimatingAppTokens.add(addPos, atoken);
+            mAnimatingAppTokens.add(addPos, wtoken);
         } else {
             // Find the item immediately above the mAppTokens insertion point and put the token
             // immediately below that one in mAnimatingAppTokens.
             final AppWindowToken aboveAnchor = mAppTokens.get(addPos + 1);
-            mAnimatingAppTokens.add(mAnimatingAppTokens.indexOf(aboveAnchor), atoken);
+            mAnimatingAppTokens.add(mAnimatingAppTokens.indexOf(aboveAnchor), wtoken);
         }
 
-        TaskList task = mTaskIdToTaskList.get(atoken.groupId);
+        TaskList task = mTaskIdToTaskList.get(wtoken.groupId);
         if (task == null) {
-            mTaskIdToTaskList.put(atoken.groupId, new TaskList(atoken));
+            task = new TaskList(wtoken, this);
+            mTaskIdToTaskList.put(wtoken.groupId, task);
+            mTaskLists.add(task);
+        } else {
+            task.mAppTokens.add(wtoken);
         }
     }
 
@@ -163,6 +171,7 @@
             AppTokenList appTokens = task.mAppTokens;
             appTokens.remove(wtoken);
             if (appTokens.size() == 0) {
+                mTaskLists.remove(task);
                 mTaskIdToTaskList.delete(taskId);
             }
         }
@@ -186,7 +195,7 @@
 
         task = mTaskIdToTaskList.get(newTaskId);
         if (task == null) {
-            task = new TaskList(wtoken);
+            task = new TaskList(wtoken, this);
             mTaskIdToTaskList.put(newTaskId, task);
         } else {
             task.mAppTokens.add(wtoken);
@@ -195,6 +204,122 @@
         wtoken.groupId = newTaskId;
     }
 
+    class TaskListsIterator implements Iterator<TaskList> {
+        private int mCur;
+        private boolean mReverse;
+
+        TaskListsIterator() {
+            this(false);
+        }
+
+        TaskListsIterator(boolean reverse) {
+            mReverse = reverse;
+            int numTaskLists = mTaskLists.size();
+            mCur = reverse ? numTaskLists - 1 : 0;
+        }
+
+        @Override
+        public boolean hasNext() {
+            if (mReverse) {
+                return mCur >= 0;
+            }
+            return mCur < mTaskLists.size();
+        }
+
+        @Override
+        public TaskList next() {
+            if (hasNext()) {
+                TaskList taskList = mTaskLists.get(mCur);
+                mCur += (mReverse ? -1 : 1);
+                return taskList;
+            }
+            throw new NoSuchElementException();
+        }
+
+        @Override
+        public void remove() {
+            throw new IllegalArgumentException();
+        }
+    }
+
+    class AppTokenIterator implements Iterator<AppWindowToken> {
+        final TaskListsIterator mIterator;
+        final boolean mReverse;
+        int mCur;
+        TaskList mTaskList;
+
+        public AppTokenIterator() {
+            this(false);
+        }
+
+        public AppTokenIterator(boolean reverse) {
+            mReverse = reverse;
+            mIterator = new TaskListsIterator(reverse);
+            getNextTaskList();
+        }
+
+        private void getNextTaskList() {
+            if (mIterator.hasNext()) {
+                mTaskList = mIterator.next();
+                mCur = mReverse ? mTaskList.mAppTokens.size() - 1 : 0;
+            }
+        }
+
+        @Override
+        public boolean hasNext() {
+            if (mTaskList == null) {
+                return false;
+            }
+            if (mReverse) {
+                return mCur >= 0;
+            }
+            return mCur < mTaskList.mAppTokens.size();
+        }
+
+        @Override
+        public AppWindowToken next() {
+            if (hasNext()) {
+                AppWindowToken wtoken = mTaskList.mAppTokens.get(mCur);
+                mCur += mReverse ? -1 : 1;
+                if (!hasNext()) {
+                    getNextTaskList();
+                }
+                return wtoken;
+            }
+            throw new NoSuchElementException();
+        }
+
+        @Override
+        public void remove() {
+            throw new IllegalArgumentException();
+        }
+    }
+
+    void verifyAppTokens() {
+        AppTokenIterator iterator = new AppTokenIterator();
+        for (int i = 0; i < mAppTokens.size(); ++i) {
+            if (!iterator.hasNext()) {
+                Slog.e(TAG, "compareAppTokens: More mAppTokens than TaskList tokens. Callers="
+                        + Debug.getCallers(4));
+                while (i < mAppTokens.size()) {
+                    Slog.e(TAG, "compareAppTokens: mAppTokens[" + i + "]=" + mAppTokens.get(i));
+                    i++;
+                }
+                return;
+            }
+            AppWindowToken appToken = mAppTokens.get(i);
+            AppWindowToken taskListToken = iterator.next();
+            if (appToken != taskListToken) {
+                Slog.e(TAG, "compareAppTokens: Mismatch at " + i + " appToken=" + appToken
+                        + " taskListToken=" + taskListToken + ". Callers=" + Debug.getCallers(4));
+            }
+        }
+        if (iterator.hasNext()) {
+            Slog.e(TAG, "compareAppTokens: More TaskList tokens than mAppTokens Callers="
+                    + Debug.getCallers(4));
+        }
+    }
+
     public void dump(String prefix, PrintWriter pw) {
         pw.print(prefix); pw.print("Display: mDisplayId="); pw.println(mDisplayId);
         final String subPrefix = "  " + prefix;
diff --git a/services/java/com/android/server/wm/TaskGroup.java b/services/java/com/android/server/wm/TaskGroup.java
new file mode 100644
index 0000000..5e82af2
--- /dev/null
+++ b/services/java/com/android/server/wm/TaskGroup.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2013 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 android.view.IApplicationToken;
+
+import java.util.ArrayList;
+
+public class TaskGroup {
+    public int taskId = -1;
+    public ArrayList<IApplicationToken> tokens = new ArrayList<IApplicationToken>();
+}
diff --git a/services/java/com/android/server/wm/TaskList.java b/services/java/com/android/server/wm/TaskList.java
index fe12b98..88791f2 100644
--- a/services/java/com/android/server/wm/TaskList.java
+++ b/services/java/com/android/server/wm/TaskList.java
@@ -16,15 +16,15 @@
 
 package com.android.server.wm;
 
-import com.android.server.am.TaskGroup;
-
-class TaskList extends TaskGroup {
+class TaskList {
 //    private final String TAG = "TaskGroup";
-    AppTokenList mAppTokens = new AppTokenList();
+    DisplayContent mDisplayContent;
+    final AppTokenList mAppTokens = new AppTokenList();
+    final int taskId;
 
-    TaskList(AppWindowToken wtoken) {
+    TaskList(AppWindowToken wtoken, DisplayContent displayContent) {
         taskId = wtoken.groupId;
-        tokens.add(wtoken.appToken);
         mAppTokens.add(wtoken);
+        mDisplayContent = displayContent;
     }
 }
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 62eccdf..3dec9a4 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -54,7 +54,6 @@
 import com.android.server.EventLogTags;
 import com.android.server.Watchdog;
 import com.android.server.am.BatteryStatsService;
-import com.android.server.am.TaskGroup;
 import com.android.server.display.DisplayManagerService;
 import com.android.server.input.InputManagerService;
 import com.android.server.power.PowerManagerService;
@@ -330,8 +329,7 @@
     /**
      * Mapping from a token IBinder to a WindowToken object.
      */
-    final HashMap<IBinder, WindowToken> mTokenMap =
-            new HashMap<IBinder, WindowToken>();
+    final HashMap<IBinder, WindowToken> mTokenMap = new HashMap<IBinder, WindowToken>();
 
     /**
      * List of window tokens that have finished starting their application,
@@ -3286,6 +3284,7 @@
                 mTaskIdToDisplayContents.put(taskId, displayContent);
             }
             displayContent.addAppToken(addPos, atoken);
+            displayContent.verifyAppTokens();
             mTokenMap.put(token.asBinder(), atoken);
             mTaskIdToDisplayContents.put(taskId, displayContent);
 
@@ -4269,11 +4268,12 @@
                         TAG, "Removing app " + wtoken + " delayed=" + delayed
                         + " animation=" + wtoken.mAppAnimator.animation
                         + " animating=" + wtoken.mAppAnimator.animating);
+                DisplayContent displayContent = mTaskIdToDisplayContents.get(wtoken.groupId);
                 if (delayed) {
                     // set the token aside because it has an active animation to be finished
                     if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG,
                             "removeAppToken make exiting: " + wtoken);
-                    mTaskIdToDisplayContents.get(wtoken.groupId).mExitingAppTokens.add(wtoken);
+                    displayContent.mExitingAppTokens.add(wtoken);
                 } else {
                     // Make sure there is no animation running on this token,
                     // so any windows associated with it will be removed as
@@ -4283,7 +4283,8 @@
                 }
                 if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG,
                         "removeAppToken: " + wtoken);
-                mTaskIdToDisplayContents.get(wtoken.groupId).removeAppToken(wtoken);
+                displayContent.removeAppToken(wtoken);
+                displayContent.verifyAppTokens();
                 wtoken.removed = true;
                 if (wtoken.startingData != null) {
                     startingToken = wtoken;
@@ -4675,6 +4676,44 @@
         Binder.restoreCallingIdentity(origId);
     }
 
+    public void moveTaskToTop(int taskId) {
+        DisplayContent displayContent = mTaskIdToDisplayContents.get(taskId);
+        if (displayContent == null) {
+            Slog.e(TAG, "moveTaskToTop: taskId=" + taskId
+                    + " not found in mTaskIdToDisplayContents");
+            return;
+        }
+        TaskList taskList = displayContent.mTaskIdToTaskList.get(taskId);
+        if (taskList == null) {
+            Slog.e(TAG, "moveTaskToTop: taskId=" + taskId + " not found in mTaskIdToTaskLists");
+            return;
+        }
+        if (!displayContent.mTaskLists.remove(taskList)) {
+            Slog.e(TAG, "moveTaskToTop: taskId=" + taskId + " not found in mTaskLists");
+        }
+        displayContent.mTaskLists.add(taskList);
+        displayContent.verifyAppTokens();
+    }
+
+    public void moveTaskToBottom(int taskId) {
+        DisplayContent displayContent = mTaskIdToDisplayContents.get(taskId);
+        if (displayContent == null) {
+            Slog.e(TAG, "moveTaskToBottom: taskId=" + taskId
+                    + " not found in mTaskIdToDisplayContents");
+            return;
+        }
+        TaskList taskList = displayContent.mTaskIdToTaskList.get(taskId);
+        if (taskList == null) {
+            Slog.e(TAG, "moveTaskToTopBottomtaskId=" + taskId + " not found in mTaskIdToTaskLists");
+            return;
+        }
+        if (!displayContent.mTaskLists.remove(taskList)) {
+            Slog.e(TAG, "moveTaskToBottom: taskId=" + taskId + " not found in mTaskLists");
+        }
+        displayContent.mTaskLists.add(0, taskList);
+        displayContent.verifyAppTokens();
+    }
+
     // -------------------------------------------------------------
     // Misc IWindowSession methods
     // -------------------------------------------------------------