Switched WindowToken/AppWindowToken to use WindowContainer

Bug: 30060889
Change-Id: Ia82aedfd9ea86410acdcd3a55a7a7fc456be2fc3
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 66fa976..518b9b5 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -18,6 +18,7 @@
 
 import static android.app.ActivityManager.StackId;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
@@ -149,29 +150,14 @@
     ArrayDeque<Rect> mFrozenBounds = new ArrayDeque<>();
     ArrayDeque<Configuration> mFrozenMergedConfig = new ArrayDeque<>();
 
-    AppWindowToken(WindowManagerService service, IApplicationToken _token, boolean _voiceInteraction) {
-        super(service, _token.asBinder(), WindowManager.LayoutParams.TYPE_APPLICATION, true);
-        appToken = _token;
+    AppWindowToken(WindowManagerService service, IApplicationToken token, boolean _voiceInteraction) {
+        super(service, token != null ? token.asBinder() : null, TYPE_APPLICATION, true);
+        appToken = token;
         voiceInteraction = _voiceInteraction;
         mInputApplicationHandle = new InputApplicationHandle(this);
         mAppAnimator = new AppWindowAnimator(this, service);
     }
 
-    void sendAppVisibilityToClients() {
-        final int count = windows.size();
-        for (int i = 0; i < count; i++) {
-            final WindowState win = windows.get(i);
-            win.sendAppVisibilityToClients(clientHidden);
-        }
-    }
-
-    void setVisibleBeforeClientHidden() {
-        for (int i = windows.size() - 1; i >= 0; i--) {
-            final WindowState w = windows.get(i);
-            w.setVisibleBeforeClientHidden();
-        }
-    }
-
     void onFirstWindowDrawn(WindowState win, WindowStateAnimator winAnimator) {
         firstWindowDrawn = true;
 
@@ -197,12 +183,12 @@
         }
 
         if (DEBUG_VISIBILITY) Slog.v(TAG, "Update reported visibility: " + this);
-        final int count = windows.size();
+        final int count = mChildren.size();
 
         mReportedVisibilityResults.reset();
 
         for (int i = 0; i < count; i++) {
-            final WindowState win = windows.get(i);
+            final WindowState win = (WindowState) mChildren.get(i);
             win.updateReportedVisibility(mReportedVisibilityResults);
         }
 
@@ -280,9 +266,9 @@
                 changed = true;
             }
 
-            final int windowsCount = windows.size();
+            final int windowsCount = mChildren.size();
             for (int i = 0; i < windowsCount; i++) {
-                final WindowState win = windows.get(i);
+                final WindowState win = (WindowState) mChildren.get(i);
                 changed |= win.onAppVisibilityChanged(visible, runningAppAnimation);
             }
 
@@ -317,8 +303,8 @@
             delayed = true;
         }
 
-        for (int i = windows.size() - 1; i >= 0 && !delayed; i--) {
-            if (windows.get(i).isWindowAnimationSet()) {
+        for (int i = mChildren.size() - 1; i >= 0 && !delayed; i--) {
+            if (((WindowState) mChildren.get(i)).isWindowAnimationSet()) {
                 delayed = true;
             }
         }
@@ -345,10 +331,10 @@
 
     WindowState findMainWindow() {
         WindowState candidate = null;
-        int j = windows.size();
+        int j = mChildren.size();
         while (j > 0) {
             j--;
-            final WindowState win = windows.get(j);
+            final WindowState win = (WindowState) mChildren.get(j);
             final int type = win.mAttrs.type;
             // No need to loop through child window as base application and starting types can't be
             // child windows.
@@ -370,27 +356,6 @@
         return StackId.canReceiveKeys(mTask.mStack.mStackId) || mAlwaysFocusable;
     }
 
-    boolean isVisible() {
-        final int count = windows.size();
-        for (int i = 0; i < count; i++) {
-            final WindowState win = windows.get(i);
-            if (win.isVisible()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    boolean canBeVisibleForCurrentUser() {
-        for (int j = windows.size() - 1; j >= 0; j--) {
-            final WindowState w = windows.get(j);
-            if (!w.isHiddenFromUserLocked()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     void removeAppFromTaskLocked() {
         mIsExiting = false;
         removeAllWindows();
@@ -408,8 +373,8 @@
 
     void clearAnimatingFlags() {
         boolean wallpaperMightChange = false;
-        for (int i = windows.size() - 1; i >= 0; i--) {
-            final WindowState win = windows.get(i);
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            final WindowState win = (WindowState) mChildren.get(i);
             wallpaperMightChange |= win.clearAnimatingFlags();
         }
         if (wallpaperMightChange) {
@@ -432,8 +397,8 @@
      */
     private void destroySurfaces(boolean cleanupOnResume) {
         final DisplayContentList displayList = new DisplayContentList();
-        for (int i = windows.size() - 1; i >= 0; i--) {
-            final WindowState win = windows.get(i);
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            final WindowState win = (WindowState) mChildren.get(i);
             final boolean destroyed = win.destroySurface(cleanupOnResume, mAppStopped);
 
             if (destroyed) {
@@ -491,9 +456,9 @@
         return allDrawn;
     }
 
-    boolean canRestoreSurfaces() {
-        for (int i = windows.size() -1; i >= 0; i--) {
-            final WindowState w = windows.get(i);
+    private boolean canRestoreSurfaces() {
+        for (int i = mChildren.size() -1; i >= 0; i--) {
+            final WindowState w = (WindowState) mChildren.get(i);
             if (w.canRestoreSurface()) {
                 return true;
             }
@@ -501,9 +466,9 @@
         return false;
     }
 
-    void clearWasVisibleBeforeClientHidden() {
-        for (int i = windows.size() - 1; i >= 0; i--) {
-            final WindowState w = windows.get(i);
+    private void clearWasVisibleBeforeClientHidden() {
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            final WindowState w = (WindowState) mChildren.get(i);
             w.clearWasVisibleBeforeClientHidden();
         }
     }
@@ -513,8 +478,8 @@
      * animating with saved surface.
      */
     boolean isAnimatingInvisibleWithSavedSurface() {
-        for (int i = windows.size() - 1; i >= 0; i--) {
-            final WindowState w = windows.get(i);
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            final WindowState w = (WindowState) mChildren.get(i);
             if (w.isAnimatingInvisibleWithSavedSurface()) {
                 return true;
             }
@@ -527,16 +492,16 @@
      * with a saved surface, and mark them destroying.
      */
     void stopUsingSavedSurfaceLocked() {
-        for (int i = windows.size() - 1; i >= 0; i--) {
-            final WindowState w = windows.get(i);
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            final WindowState w = (WindowState) mChildren.get(i);
             w.stopUsingSavedSurface();
         }
         destroySurfaces();
     }
 
     void markSavedSurfaceExiting() {
-        for (int i = windows.size() - 1; i >= 0; i--) {
-            final WindowState w = windows.get(i);
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            final WindowState w = (WindowState) mChildren.get(i);
             w.markSavedSurfaceExiting();
         }
     }
@@ -550,8 +515,8 @@
         // Check if all interesting windows are drawn and we can mark allDrawn=true.
         int interestingNotDrawn = -1;
 
-        for (int i = windows.size() - 1; i >= 0; i--) {
-            final WindowState w = windows.get(i);
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            final WindowState w = (WindowState) mChildren.get(i);
             interestingNotDrawn = w.restoreSavedSurfaceForInterestingWindow();
         }
 
@@ -569,8 +534,8 @@
     }
 
     void destroySavedSurfaces() {
-        for (int i = windows.size() - 1; i >= 0; i--) {
-            final WindowState win = windows.get(i);
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            final WindowState win = (WindowState) mChildren.get(i);
             win.destroySavedSurface();
         }
     }
@@ -589,12 +554,12 @@
         if (startingWindow == win) {
             if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Notify removed startingWindow " + win);
             mService.scheduleRemoveStartingWindowLocked(this);
-        } else if (windows.size() == 0 && startingData != null) {
+        } else if (mChildren.size() == 0 && startingData != null) {
             // If this is the last window and we had requested a starting transition window,
             // well there is no point now.
             if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Nulling last startingWindow");
             startingData = null;
-        } else if (windows.size() == 1 && startingView != null) {
+        } else if (mChildren.size() == 1 && startingView != null) {
             // If this is the last window except for a starting transition window,
             // we need to get rid of the starting transition.
             mService.scheduleRemoveStartingWindowLocked(this);
@@ -602,14 +567,8 @@
     }
 
     void removeDeadWindows() {
-        for (int winNdx = windows.size() - 1; winNdx >= 0;
-            // WindowState#removeIfPossible() at bottom of loop may remove multiple entries from
-            // windows if the window to be removed has child windows. It also may
-            // not remove any windows from windows at all if win is exiting and
-            // currently animating away. This ensures that winNdx is monotonically decreasing
-            // and never beyond windows bounds.
-            winNdx = Math.min(winNdx - 1, windows.size() - 1)) {
-            WindowState win = windows.get(winNdx);
+        for (int winNdx = mChildren.size() - 1; winNdx >= 0; --winNdx) {
+            WindowState win = (WindowState) mChildren.get(winNdx);
             if (win.mAppDied) {
                 if (DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.w(TAG,
                         "removeDeadWindows: " + win);
@@ -622,10 +581,10 @@
     }
 
     boolean hasWindowsAlive() {
-        for (int i = windows.size() - 1; i >= 0; i--) {
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
             // No need to loop through child windows as the answer should be the same as that of the
             // parent window.
-            if (!windows.get(i).mAppDied) {
+            if (!((WindowState) mChildren.get(i)).mAppDied) {
                 return true;
             }
         }
@@ -636,8 +595,8 @@
         if (DEBUG_ADD_REMOVE) Slog.d(TAG_WM,
                 "Marking app token " + this + " with replacing windows.");
 
-        for (int i = windows.size() - 1; i >= 0; i--) {
-            final WindowState w = windows.get(i);
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            final WindowState w = (WindowState) mChildren.get(i);
             w.setWillReplaceWindow(animate);
         }
         if (animate) {
@@ -653,8 +612,8 @@
     void setWillReplaceChildWindows() {
         if (DEBUG_ADD_REMOVE) Slog.d(TAG_WM, "Marking app token " + this
                 + " with replacing child windows.");
-        for (int i = windows.size() - 1; i >= 0; i--) {
-            final WindowState w = windows.get(i);
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            final WindowState w = (WindowState) mChildren.get(i);
             w.setWillReplaceChildWindows();
         }
     }
@@ -663,15 +622,15 @@
         if (DEBUG_ADD_REMOVE) Slog.d(TAG_WM,
                 "Resetting app token " + this + " of replacing window marks.");
 
-        for (int i = windows.size() - 1; i >= 0; i--) {
-            final WindowState w = windows.get(i);
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            final WindowState w = (WindowState) mChildren.get(i);
             w.clearWillReplaceWindow();
         }
     }
 
     void requestUpdateWallpaperIfNeeded() {
-        for (int i = windows.size() - 1; i >= 0; i--) {
-            final WindowState w = windows.get(i);
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            final WindowState w = (WindowState) mChildren.get(i);
             w.requestUpdateWallpaperIfNeeded();
         }
     }
@@ -711,8 +670,8 @@
         super.addWindow(w);
 
         boolean gotReplacementWindow = false;
-        for (int i = windows.size() - 1; i >= 0; i--) {
-            final WindowState candidate = windows.get(i);
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            final WindowState candidate = (WindowState) mChildren.get(i);
             gotReplacementWindow |= candidate.setReplacementWindowIfNeeded(w);
         }
 
@@ -723,8 +682,8 @@
     }
 
     boolean waitingForReplacement() {
-        for (int i = windows.size() - 1; i >= 0; i--) {
-            final WindowState candidate = windows.get(i);
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            final WindowState candidate = (WindowState) mChildren.get(i);
             if (candidate.waitingForReplacement()) {
                 return true;
             }
@@ -733,8 +692,8 @@
     }
 
     void onWindowReplacementTimeout() {
-        for (int i = windows.size() - 1; i >= 0; --i) {
-            windows.get(i).onWindowReplacementTimeout();
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            ((WindowState) mChildren.get(i)).onWindowReplacementTimeout();
         }
     }
 
@@ -774,8 +733,8 @@
         if (!mFrozenMergedConfig.isEmpty()) {
             mFrozenMergedConfig.remove();
         }
-        for (int i = windows.size() - 1; i >= 0; i--) {
-            final WindowState win = windows.get(i);
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            final WindowState win = (WindowState) mChildren.get(i);
             win.onUnfreezeBounds();
         }
         mService.mWindowPlacerLocked.performSurfacePlacement();
@@ -824,80 +783,23 @@
     }
 
     void resetJustMovedInStack() {
-        for (int i = windows.size() - 1; i >= 0; i--) {
-            windows.get(i).resetJustMovedInStack();
-        }
-    }
-
-    void setWaitingForDrawnIfResizingChanged() {
-        for (int i = windows.size() - 1; i >= 0; --i) {
-            final WindowState win = windows.get(i);
-            win.setWaitingForDrawnIfResizingChanged();
-        }
-    }
-
-    void resizeWindows() {
-        for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
-            final WindowState win = windows.get(winNdx);
-            win.addToResizingList();
-        }
-    }
-
-    void setMovedByResize() {
-        for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
-            final WindowState win = windows.get(winNdx);
-            win.setMovedByResize();
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            ((WindowState) mChildren.get(i)).resetJustMovedInStack();
         }
     }
 
     void notifyMovedInStack() {
-        for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
-            final WindowState win = windows.get(winNdx);
+        for (int winNdx = mChildren.size() - 1; winNdx >= 0; --winNdx) {
+            final WindowState win = (WindowState) mChildren.get(winNdx);
             win.notifyMovedInStack();
         }
     }
 
-    void resetDragResizingChangeReported() {
-        for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
-            final WindowState win = windows.get(winNdx);
-            win.resetDragResizingChangeReported();
-        }
-    }
-
-    void detachDisplay() {
-        boolean doAnotherLayoutPass = false;
-        for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
-            // We are in the middle of changing the state of displays/stacks/tasks. We need
-            // to finish that, before we let layout interfere with it.
-            // Also removes child windows.
-            windows.get(winNdx).removeIfPossible();
-            doAnotherLayoutPass = true;
-        }
-        if (doAnotherLayoutPass) {
-            mService.mWindowPlacerLocked.requestTraversal();
-        }
-    }
-
-    void forceWindowsScaleableInTransaction(boolean force) {
-        for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
-            windows.get(winNdx).forceWindowsScaleableInTransaction(force);
-        }
-    }
-
-    boolean isAnimating() {
-        for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
-            if (windows.get(winNdx).isAnimating()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     void setAppLayoutChanges(int changes, String reason, int displayId) {
         final WindowAnimator windowAnimator = mAppAnimator.mAnimator;
-        for (int i = windows.size() - 1; i >= 0; i--) {
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
             // Child windows will be on the same display as their parents.
-            if (displayId == windows.get(i).getDisplayId()) {
+            if (displayId == ((WindowState) mChildren.get(i)).getDisplayId()) {
                 windowAnimator.setPendingLayoutChanges(displayId, changes);
                 if (DEBUG_LAYOUT_REPEATS) {
                     mService.mWindowPlacerLocked.debugLayoutRepeats(
@@ -909,8 +811,8 @@
     }
 
     void removeReplacedWindowIfNeeded(WindowState replacement) {
-        for (int i = windows.size() - 1; i >= 0; i--) {
-            final WindowState win = windows.get(i);
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            final WindowState win = (WindowState) mChildren.get(i);
             if (win.removeReplacedWindowIfNeeded(replacement)) {
                 return;
             }
@@ -932,9 +834,9 @@
                     mService.mH.sendEmptyMessageDelayed(H.APP_FREEZE_TIMEOUT, 2000);
                 }
             }
-            final int count = windows.size();
+            final int count = mChildren.size();
             for (int i = 0; i < count; i++) {
-                final WindowState w = windows.get(i);
+                final WindowState w = (WindowState) mChildren.get(i);
                 w.onStartFreezingScreen();
             }
         }
@@ -945,10 +847,10 @@
             return;
         }
         if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Clear freezing of " + this + " force=" + force);
-        final int count = windows.size();
+        final int count = mChildren.size();
         boolean unfrozeWindows = false;
         for (int i = 0; i < count; i++) {
-            final WindowState w = windows.get(i);
+            final WindowState w = (WindowState) mChildren.get(i);
             unfrozeWindows |= w.onStopFreezingScreen();
         }
         if (force || unfrozeWindows) {
@@ -1065,16 +967,16 @@
     }
 
     boolean isLastWindow(WindowState win) {
-        return windows.size() == 1 && windows.get(0) == win;
+        return mChildren.size() == 1 && mChildren.get(0) == win;
     }
 
     void setAllAppWinAnimators() {
         final ArrayList<WindowStateAnimator> allAppWinAnimators = mAppAnimator.mAllAppWinAnimators;
         allAppWinAnimators.clear();
 
-        final int windowsCount = windows.size();
+        final int windowsCount = mChildren.size();
         for (int j = 0; j < windowsCount; j++) {
-            windows.get(j).addWinAnimatorToList(allAppWinAnimators);
+            ((WindowState) mChildren.get(j)).addWinAnimatorToList(allAppWinAnimators);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 26be088..990405a 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -624,7 +624,7 @@
      */
     TaskStack getDockedStackLocked() {
         final TaskStack stack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
-        return (stack != null && stack.isVisibleLocked()) ? stack : null;
+        return (stack != null && stack.isVisible()) ? stack : null;
     }
 
     /**
@@ -633,7 +633,7 @@
      */
     TaskStack getDockedStackVisibleForUserLocked() {
         final TaskStack stack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
-        return (stack != null && stack.isVisibleLocked(true /* ignoreKeyguard */)) ? stack : null;
+        return (stack != null && stack.isVisible(true /* ignoreKeyguard */)) ? stack : null;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index cd576734..de8e5ac 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -541,7 +541,7 @@
         final ArrayList<Task> homeStackTasks = homeStack.getTasks();
         final Task topHomeStackTask = homeStackTasks.get(homeStackTasks.size() - 1);
         final boolean homeVisible = homeTask.getTopVisibleAppToken() != null;
-        final boolean homeBehind = (fullscreenStack != null && fullscreenStack.isVisibleLocked())
+        final boolean homeBehind = (fullscreenStack != null && fullscreenStack.isVisible())
                 || (homeStackTasks.size() > 1 && topHomeStackTask != homeTask);
         // If the home task is an on-top launcher, we don't want to minimize the docked stack.
         // Instead we want everything underneath that was visible to remain visible.
@@ -639,7 +639,7 @@
         final ArrayList<TaskStack> stacks = mDisplayContent.getStacks();
         for (int i = stacks.size() - 1; i >= 0; --i) {
             final TaskStack stack = stacks.get(i);
-            if (stack.isVisibleLocked() && stack.isAdjustedForIme()) {
+            if (stack.isVisible() && stack.isAdjustedForIme()) {
                 stack.beginImeAdjustAnimation();
             }
         }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 3451333..4241b32 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -304,9 +304,9 @@
             return false;
         }
         if ((boundsChanged & BOUNDS_CHANGE_SIZE) == BOUNDS_CHANGE_SIZE) {
-            resizeWindows();
+            onResize();
         } else {
-            setMovedByResize();
+            onMovedByResize();
         }
         return true;
     }
@@ -469,14 +469,14 @@
         }
     }
 
-    void resetDragResizingChangeReported() {
+    private void resetDragResizingChangeReported() {
         for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) {
             mAppTokens.get(activityNdx).resetDragResizingChangeReported();
         }
     }
 
     boolean isDragResizing() {
-        return mDragResizing || (mStack != null && mStack.isDragResizing());
+        return mDragResizing;
     }
 
     int getDragResizeMode() {
@@ -493,10 +493,12 @@
         }
     }
 
-    void detachDisplay() {
+    boolean detachFromDisplay() {
+        boolean didSomething = false;
         for (int i = mAppTokens.size() - 1; i >= 0; --i) {
-            mAppTokens.get(i).detachDisplay();
+            didSomething |= mAppTokens.get(i).detachFromDisplay();
         }
+        return didSomething;
     }
 
     void updateDisplayInfo(final DisplayContent displayContent) {
@@ -534,15 +536,15 @@
         }
     }
 
-    void resizeWindows() {
+    private void onResize() {
         for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) {
-            mAppTokens.get(activityNdx).resizeWindows();
+            mAppTokens.get(activityNdx).onResize();
         }
     }
 
-    void setMovedByResize() {
+    private void onMovedByResize() {
         for (int i = mAppTokens.size() - 1; i >= 0; --i) {
-            mAppTokens.get(i).setMovedByResize();
+            mAppTokens.get(i).onMovedByResize();
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 254a0d3..21fc08b 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -113,9 +113,6 @@
     /** Detach this stack from its display when animation completes. */
     boolean mDeferDetach;
 
-    // Whether the stack and all its tasks is currently being drag-resized
-    private boolean mDragResizing;
-
     private final Rect mTmpAdjustedBounds = new Rect();
     private boolean mAdjustedForIme;
     private boolean mImeGoingAway;
@@ -656,7 +653,7 @@
             throw new IllegalStateException(
                     "Calling getStackDockedModeBoundsLocked() when there is no docked stack.");
         }
-        if (!ignoreVisibility && !dockedStack.isVisibleLocked()) {
+        if (!ignoreVisibility && !dockedStack.isVisible()) {
             // The docked stack is being dismissed, but we caught before it finished being
             // dismissed. In that case we want to treat it as if it is not occupying any space and
             // let others occupy the whole display.
@@ -762,14 +759,16 @@
                 1 /*allowResizeInDockedMode*/, bounds).sendToTarget();
     }
 
-    void detachDisplay() {
+    boolean detachFromDisplay() {
         EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId);
 
+        boolean didSomething = false;
         for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
-            mTasks.get(taskNdx).detachDisplay();
+            didSomething |= mTasks.get(taskNdx).detachFromDisplay();
         }
 
         close();
+        return didSomething;
     }
 
     void resetAnimationBackgroundAnimator() {
@@ -846,7 +845,7 @@
             mAdjustImeAmount = adjustAmount;
             mAdjustDividerAmount = adjustDividerAmount;
             updateAdjustedBounds();
-            return isVisibleLocked(true /* ignoreKeyguard */);
+            return isVisible(true /* ignoreKeyguard */);
         } else {
             return false;
         }
@@ -882,7 +881,7 @@
         if (minimizeAmount != mMinimizeAmount) {
             mMinimizeAmount = minimizeAmount;
             updateAdjustedBounds();
-            return isVisibleLocked(true /* ignoreKeyguard*/);
+            return isVisible(true /* ignoreKeyguard */);
         } else {
             return false;
         }
@@ -1188,11 +1187,11 @@
         }
     }
 
-    boolean isVisibleLocked() {
-        return isVisibleLocked(false /* ignoreKeyguard */);
+    boolean isVisible() {
+        return isVisible(false /* ignoreKeyguard */);
     }
 
-    boolean isVisibleLocked(boolean ignoreKeyguard) {
+    boolean isVisible(boolean ignoreKeyguard) {
         final boolean keyguardOn = mService.mPolicy.isKeyguardShowingOrOccluded()
                 && !mService.mAnimator.mKeyguardGoingAway;
         if (!ignoreKeyguard && keyguardOn && !StackId.isAllowedOverLockscreen(mStackId)) {
@@ -1212,20 +1211,6 @@
         return false;
     }
 
-    boolean isDragResizing() {
-        return mDragResizing;
-    }
-
-    void setDragResizingLocked(boolean resizing) {
-        if (mDragResizing == resizing) {
-            return;
-        }
-        mDragResizing = resizing;
-        for (int i = mTasks.size() - 1; i >= 0 ; i--) {
-            mTasks.get(i).resetDragResizingChangeReported();
-        }
-    }
-
     @Override  // AnimatesBounds
     public boolean setSize(Rect bounds) {
         synchronized (mService.mWindowMap) {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index fc4fdfa..fd6994c 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -92,6 +92,84 @@
         return false;
     }
 
+    void setWaitingForDrawnIfResizingChanged() {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = mChildren.get(i);
+            wc.setWaitingForDrawnIfResizingChanged();
+        }
+    }
+
+    void onResize() {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = mChildren.get(i);
+            wc.onResize();
+        }
+    }
+
+    void onMovedByResize() {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = mChildren.get(i);
+            wc.onMovedByResize();
+        }
+    }
+
+    void resetDragResizingChangeReported() {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = mChildren.get(i);
+            wc.resetDragResizingChangeReported();
+        }
+    }
+
+    boolean detachFromDisplay() {
+        boolean didSomething = false;
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = mChildren.get(i);
+            didSomething |= wc.detachFromDisplay();
+        }
+        return didSomething;
+    }
+
+    void forceWindowsScaleableInTransaction(boolean force) {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = mChildren.get(i);
+            wc.forceWindowsScaleableInTransaction(force);
+        }
+    }
+
+    boolean isAnimating() {
+        for (int j = mChildren.size() - 1; j >= 0; j--) {
+            final WindowContainer wc = mChildren.get(j);
+            if (wc.isAnimating()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    void sendAppVisibilityToClients() {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = mChildren.get(i);
+            wc.sendAppVisibilityToClients();
+        }
+    }
+
+    void setVisibleBeforeClientHidden() {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = mChildren.get(i);
+            wc.setVisibleBeforeClientHidden();
+        }
+    }
+
+    boolean isVisible() {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = mChildren.get(i);
+            if (wc.isVisible()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     /** Returns the top child container or this container if there are no children. */
     WindowContainer getTop() {
         return mChildren.isEmpty() ? this : mChildren.peekLast();
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d4750e7..851f8d8 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -4011,7 +4011,7 @@
 
     boolean isStackVisibleLocked(int stackId) {
         final TaskStack stack = mStackIdToStack.get(stackId);
-        return (stack != null && stack.isVisibleLocked());
+        return (stack != null && stack.isVisible());
     }
 
     public void setDockedStackCreateState(int mode, Rect bounds) {
@@ -4081,7 +4081,9 @@
 
     void detachStackLocked(DisplayContent displayContent, TaskStack stack) {
         displayContent.detachStack(stack);
-        stack.detachDisplay();
+        if (stack.detachFromDisplay()) {
+            mWindowPlacerLocked.requestTraversal();
+        }
         if (stack.mStackId == DOCKED_STACK_ID) {
             getDefaultDisplayContentLocked().mDividerControllerLocked
                     .notifyDockedStackExistsChanged(false);
@@ -4225,7 +4227,7 @@
                         + " not found.");
             }
             if (stack.setBounds(bounds, configs, taskBounds, taskTempInsetBounds)
-                    && stack.isVisibleLocked()) {
+                    && stack.isVisible()) {
                 stack.getDisplayContent().layoutNeeded = true;
                 mWindowPlacerLocked.performSurfacePlacement();
             }
@@ -6792,7 +6794,7 @@
             for (int i = stacks.size() - 1; i >= 0; --i) {
                 final TaskStack stack = stacks.get(i);
                 final boolean isDockedOnBottom = stack.getDockSide() == DOCKED_BOTTOM;
-                if (stack.isVisibleLocked() && (imeOnBottom || isDockedOnBottom)) {
+                if (stack.isVisible() && (imeOnBottom || isDockedOnBottom)) {
                     stack.setAdjustedForIme(imeWin, imeOnBottom && imeHeightChanged);
                 } else {
                     stack.resetAdjustedForIme(false);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index dea06a9..2c359fc 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1172,6 +1172,7 @@
 
     // TODO: Sigh...another is visible method...tried to consolidate with other isVisible methods
     // below, but failed. Need to figure-out a good way to handle this long term...
+    @Override
     boolean isVisible() {
         // If we're animating with a saved surface, we're already visible.
         // Return true so that the alpha doesn't get cleared.
@@ -1181,13 +1182,7 @@
             return true;
         }
 
-        for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowState c = (WindowState) mChildren.get(i);
-            if (c.isVisible()) {
-                return true;
-            }
-        }
-        return false;
+        return super.isVisible();
     }
 
     /**
@@ -1427,14 +1422,11 @@
                 && (mAppToken == null || mAppToken.mAppAnimator.animation == null);
     }
 
-    void setMovedByResize() {
-        if (DEBUG_RESIZE) Slog.d(TAG, "setMovedByResize: Moving " + this);
+    @Override
+    void onMovedByResize() {
+        if (DEBUG_RESIZE) Slog.d(TAG, "onMovedByResize: Moving " + this);
         mMovedByResize = true;
-
-        for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowState c = (WindowState) mChildren.get(i);
-            c.setMovedByResize();
-        }
+        super.onMovedByResize();
     }
 
     boolean onAppVisibilityChanged(boolean visible, boolean runningAppAnimation) {
@@ -1499,14 +1491,15 @@
         return changed;
     }
 
-    void addToResizingList() {
+    @Override
+    void onResize() {
         // Some windows won't go through the resizing process, if they don't have a surface, so
         // destroy all saved surfaces here.
         destroySavedSurface();
 
         final ArrayList<WindowState> resizingWindows = mService.mResizingWindows;
         if (mHasSurface && !resizingWindows.contains(this)) {
-            if (DEBUG_RESIZE) Slog.d(TAG, "resizeWindows: Resizing " + this);
+            if (DEBUG_RESIZE) Slog.d(TAG, "onResize: Resizing " + this);
             resizingWindows.add(this);
 
             // If we are not drag resizing, force recreating of a new surface so updating
@@ -1529,10 +1522,7 @@
             mResizedWhileGone = true;
         }
 
-        for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowState c = (WindowState) mChildren.get(i);
-            c.addToResizingList();
-        }
+        super.onResize();
     }
 
     void onUnfreezeBounds() {
@@ -1618,15 +1608,13 @@
         }
     }
 
+    @Override
     void forceWindowsScaleableInTransaction(boolean force) {
         if (mWinAnimator != null && mWinAnimator.hasSurface()) {
             mWinAnimator.mSurfaceController.forceScaleableInTransaction(force);
         }
 
-        for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowState c = (WindowState) mChildren.get(i);
-            c.forceWindowsScaleableInTransaction(force);
-        }
+        super.forceWindowsScaleableInTransaction(force);
     }
 
     @Override
@@ -1815,6 +1803,15 @@
         Binder.restoreCallingIdentity(origId);
     }
 
+    @Override
+    boolean detachFromDisplay() {
+        // We are in the middle of changing the state of displays/stacks/tasks. We need
+        // to finish that, before we let layout interfere with it.
+        // Also removes child windows.
+        removeIfPossible();
+        return true;
+    }
+
     private void setupWindowForRemoveOnExit() {
         mRemoveOnExit = true;
         setDisplayLayoutNeeded();
@@ -2376,17 +2373,12 @@
         return mAnimatingWithSavedSurface;
     }
 
+    @Override
     boolean isAnimating() {
         if (mWinAnimator.isAnimationSet() || mAnimatingExit) {
             return true;
         }
-        for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowState c = (WindowState) mChildren.get(i);
-            if (c.isAnimating()) {
-                return true;
-            }
-        }
-        return false;
+        return super.isAnimating();
     }
 
     boolean isAnimatingInvisibleWithSavedSurface() {
@@ -2440,12 +2432,10 @@
         }
     }
 
-    void sendAppVisibilityToClients(boolean clientHidden) {
-        for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowState c = (WindowState) mChildren.get(i);
-            c.sendAppVisibilityToClients(clientHidden);
-        }
+    void sendAppVisibilityToClients() {
+        super.sendAppVisibilityToClients();
 
+        final boolean clientHidden = mAppToken.clientHidden;
         if (mAttrs.type == TYPE_APPLICATION_STARTING && clientHidden) {
             // Don't hide the starting window.
             return;
@@ -2463,10 +2453,7 @@
         mWasVisibleBeforeClientHidden |=
                 (mViewVisibility == View.VISIBLE || mAnimatingWithSavedSurface);
 
-        for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowState c = (WindowState) mChildren.get(i);
-            c.setVisibleBeforeClientHidden();
-        }
+        super.setVisibleBeforeClientHidden();
     }
 
     public void clearWasVisibleBeforeClientHidden() {
@@ -3028,14 +3015,12 @@
         return mDragResizing != computeDragResizing();
     }
 
+    @Override
     void setWaitingForDrawnIfResizingChanged() {
         if (isDragResizeChanged()) {
             mService.mWaitingForDrawn.add(this);
         }
-        for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowState c = (WindowState) mChildren.get(i);
-            c.setWaitingForDrawnIfResizingChanged();
-        }
+        super.setWaitingForDrawnIfResizingChanged();
     }
 
     /**
@@ -3048,12 +3033,10 @@
     /**
      * Resets the state whether we reported a drag resize change to the app.
      */
+    @Override
     void resetDragResizingChangeReported() {
         mDragResizingChangeReported = false;
-        for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowState c = (WindowState) mChildren.get(i);
-            c.resetDragResizingChangeReported();
-        }
+        super.resetDragResizingChangeReported();
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 553b056..5f9bb9a 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -42,12 +42,11 @@
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
 
 /**
- * Container of a set of related windows in the window manager.  Often this
- * is an AppWindowToken, which is the handle for an Activity that it uses
- * to display windows.  For nested windows, there is a WindowToken created for
- * the parent window to manage its children.
+ * Container of a set of related windows in the window manager. Often this is an AppWindowToken,
+ * which is the handle for an Activity that it uses to display windows. For nested windows, there is
+ * a WindowToken created for the parent window to manage its children.
  */
-class WindowToken {
+class WindowToken extends WindowContainer {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowToken" : TAG_WM;
 
     // The window manager!
@@ -66,9 +65,6 @@
     // For printing.
     String stringName;
 
-    // All of the windows associated with this token.
-    protected final WindowList windows = new WindowList();
-
     // Is key dispatching paused for this token?
     boolean paused = false;
 
@@ -95,12 +91,12 @@
     }
 
     void removeAllWindows() {
-        for (int i = windows.size() - 1; i >= 0; --i) {
-            final WindowState win = windows.get(i);
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowState win = (WindowState) mChildren.get(i);
             if (DEBUG_WINDOW_MOVEMENT) Slog.w(TAG_WM, "removeAllWindows: removing win=" + win);
             win.removeIfPossible();
         }
-        windows.clear();
+        mChildren.clear();
     }
 
     void setExiting() {
@@ -108,13 +104,13 @@
             return;
         }
 
-        final int count = windows.size();
+        final int count = mChildren.size();
         boolean changed = false;
         boolean delayed = false;
         DisplayContent displayContent = null;
 
         for (int i = 0; i < count; i++) {
-            final WindowState win = windows.get(i);
+            final WindowState win = (WindowState) mChildren.get(i);
             if (win.mWinAnimator.isAnimationSet()) {
                 delayed = true;
                 // TODO: This is technically wrong as a token can have windows on multi-displays
@@ -138,8 +134,8 @@
 
     int adjustAnimLayer(int adj) {
         int highestAnimLayer = -1;
-        for (int j = windows.size() - 1; j >= 0; j--) {
-            final WindowState w = windows.get(j);
+        for (int j = mChildren.size() - 1; j >= 0; j--) {
+            final WindowState w = (WindowState) mChildren.get(j);
             final int winHighestAnimLayer = w.adjustAnimLayer(adj);
             if (winHighestAnimLayer > highestAnimLayer) {
                 highestAnimLayer = winHighestAnimLayer;
@@ -151,11 +147,12 @@
         return highestAnimLayer;
     }
 
-    private WindowState getTopWindow() {
-        if (windows.isEmpty()) {
+    /* package level access for test. */
+    WindowState getTopWindow() {
+        if (mChildren.isEmpty()) {
             return null;
         }
-        return (WindowState) windows.get(windows.size() - 1).getTop();
+        return (WindowState) mChildren.get(mChildren.size() - 1).getTop();
     }
 
     /**
@@ -163,9 +160,10 @@
      * @param target The window to search for.
      * @return The index of win in windows or of the window that is an ancestor of win.
      */
-    private int getWindowIndex(WindowState target) {
-        for (int i = windows.size() - 1; i >= 0; --i) {
-            final WindowState w = windows.get(i);
+    /* package level access for test. */
+    int getWindowIndex(WindowState target) {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowState w = (WindowState) mChildren.get(i);
             if (w == target || w.hasChild(target)) {
                 return i;
             }
@@ -250,9 +248,9 @@
             } else {
                 win.addNonAppWindowToList();
             }
-            if (!windows.contains(win)) {
+            if (!mChildren.contains(win)) {
                 if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "Adding " + win + " to " + this);
-                windows.add(tokenWindowsPos, win);
+                mChildren.add(tokenWindowsPos, win);
             }
         } else {
             addChildWindow(win);
@@ -424,9 +422,9 @@
     }
 
     int reAddAppWindows(DisplayContent displayContent, int index) {
-        final int count = windows.size();
+        final int count = mChildren.size();
         for (int i = 0; i < count; i++) {
-            final WindowState win = windows.get(i);
+            final WindowState win = (WindowState) mChildren.get(i);
             final DisplayContent winDisplayContent = win.getDisplayContent();
             if (winDisplayContent == displayContent || winDisplayContent == null) {
                 win.mDisplayContent = displayContent;
@@ -441,9 +439,9 @@
      * ordering the windows in mWindows
      */
     private int findIdxBasedOnAppTokens(WindowState win) {
-        WindowList windows = win.getWindowList();
+        final WindowList windows = win.getWindowList();
         for(int j = windows.size() - 1; j >= 0; j--) {
-            WindowState wentry = windows.get(j);
+            final WindowState wentry = windows.get(j);
             if(wentry.mAppToken == win.mAppToken) {
                 return j;
             }
@@ -453,10 +451,10 @@
 
     /** Return the first window in the token window list that isn't a starting window or null. */
     WindowState getFirstNonStartingWindow() {
-        final int count = windows.size();
+        final int count = mChildren.size();
         // We only care about parent windows so no need to loop through child windows.
         for (int i = 0; i < count; i++) {
-            final WindowState w = windows.get(i);
+            final WindowState w = (WindowState) mChildren.get(i);
             if (w.mAttrs.type != TYPE_APPLICATION_STARTING) {
                 return w;
             }
@@ -466,17 +464,17 @@
 
     @CallSuper
     void removeWindow(WindowState win) {
-        windows.remove(win);
+        mChildren.remove(win);
     }
 
     /** Returns true if the token windows list is empty. */
     boolean isEmpty() {
-        return windows.isEmpty();
+        return mChildren.isEmpty();
     }
 
     WindowState getReplacingWindow() {
-        for (int i = windows.size() - 1; i >= 0; i--) {
-            final WindowState win = windows.get(i);
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            final WindowState win = (WindowState) mChildren.get(i);
             final WindowState replacing = win.getReplacingWindow();
             if (replacing != null) {
                 return replacing;
@@ -486,8 +484,8 @@
     }
 
     void hideWallpaperToken(boolean wasDeferred, String reason) {
-        for (int j = windows.size() - 1; j >= 0; j--) {
-            final WindowState wallpaper = windows.get(j);
+        for (int j = mChildren.size() - 1; j >= 0; j--) {
+            final WindowState wallpaper = (WindowState) mChildren.get(j);
             wallpaper.hideWallpaperWindow(wasDeferred, reason);
         }
         hidden = true;
@@ -495,8 +493,8 @@
 
     void sendWindowWallpaperCommand(
             String action, int x, int y, int z, Bundle extras, boolean sync) {
-        for (int wallpaperNdx = windows.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
-            WindowState wallpaper = windows.get(wallpaperNdx);
+        for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
+            final WindowState wallpaper = (WindowState) mChildren.get(wallpaperNdx);
             try {
                 wallpaper.mClient.dispatchWallpaperCommand(action, x, y, z, extras, sync);
                 // We only want to be synchronous with one wallpaper.
@@ -508,8 +506,8 @@
 
     void updateWallpaperOffset(int dw, int dh, boolean sync) {
         final WallpaperController wallpaperController = mService.mWallpaperControllerLocked;
-        for (int wallpaperNdx = windows.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
-            WindowState wallpaper = windows.get(wallpaperNdx);
+        for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
+            final WindowState wallpaper = (WindowState) mChildren.get(wallpaperNdx);
             if (wallpaperController.updateWallpaperOffset(wallpaper, dw, dh, sync)) {
                 final WindowStateAnimator winAnimator = wallpaper.mWinAnimator;
                 winAnimator.computeShownFrameLocked();
@@ -529,8 +527,8 @@
         }
 
         final WallpaperController wallpaperController = mService.mWallpaperControllerLocked;
-        for (int wallpaperNdx = windows.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
-            WindowState wallpaper = windows.get(wallpaperNdx);
+        for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
+            final WindowState wallpaper = (WindowState) mChildren.get(wallpaperNdx);
             if (visible) {
                 wallpaperController.updateWallpaperOffset(wallpaper, dw, dh, false);
             }
@@ -552,8 +550,8 @@
         }
 
         final WallpaperController wallpaperController = mService.mWallpaperControllerLocked;
-        for (int wallpaperNdx = windows.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
-            final WindowState wallpaper = windows.get(wallpaperNdx);
+        for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
+            final WindowState wallpaper = (WindowState) mChildren.get(wallpaperNdx);
 
             if (visible) {
                 wallpaperController.updateWallpaperOffset(wallpaper, dw, dh, false);
@@ -628,8 +626,8 @@
     }
 
     boolean hasVisibleNotDrawnWallpaper() {
-        for (int j = windows.size() - 1; j >= 0; --j) {
-            final WindowState wallpaper = windows.get(j);
+        for (int j = mChildren.size() - 1; j >= 0; --j) {
+            final WindowState wallpaper = (WindowState) mChildren.get(j);
             if (wallpaper.hasVisibleNotDrawnWallpaper()) {
                 return true;
             }
@@ -639,8 +637,8 @@
 
     int getHighestAnimLayer() {
         int highest = -1;
-        for (int j = 0; j < windows.size(); j++) {
-            final WindowState w = windows.get(j);
+        for (int j = 0; j < mChildren.size(); j++) {
+            final WindowState w = (WindowState) mChildren.get(j);
             final int wLayer = w.getHighestAnimLayer();
             if (wLayer > highest) {
                 highest = wLayer;
@@ -656,7 +654,7 @@
     }
 
     void dump(PrintWriter pw, String prefix) {
-        pw.print(prefix); pw.print("windows="); pw.println(windows);
+        pw.print(prefix); pw.print("windows="); pw.println(mChildren);
         pw.print(prefix); pw.print("windowType="); pw.print(windowType);
                 pw.print(" hidden="); pw.print(hidden);
                 pw.print(" hasVisible="); pw.println(hasVisible);
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
new file mode 100644
index 0000000..4050795
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -0,0 +1,94 @@
+/*
+ * 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 android.content.Context;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.IWindow;
+import android.view.WindowManager;
+import android.view.WindowManagerPolicy;
+
+import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+
+/**
+ * Tests for the {@link WindowState} class.
+ *
+ * Build: mmma -j32 frameworks/base/services/tests/servicestests
+ * Install: adb install -r out/target/product/$TARGET_PRODUCT/data/app/FrameworksServicesTests/FrameworksServicesTests.apk
+ * Run: adb shell am instrument -w -e class com.android.server.wm.AppWindowTokenTests com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
+ */
+@SmallTest
+public class AppWindowTokenTests extends AndroidTestCase {
+
+    private static WindowManagerService sWm = null;
+    private final WindowManagerPolicy mPolicy = new TestWindowManagerPolicy();
+    private final IWindow mIWindow = new TestIWindow();
+
+    @Override
+    public void setUp() throws Exception {
+        final Context context = getContext();
+        if (sWm == null) {
+            // We only want to do this once for the test process as we don't want WM to try to
+            // register a bunch of local services again.
+            sWm = WindowManagerService.main(context, null, true, false, false, mPolicy);
+        }
+    }
+
+    public void testFindMainWindow() throws Exception {
+        final TestAppWindowToken token = new TestAppWindowToken();
+
+        assertNull(token.findMainWindow());
+
+        final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, token);
+        final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, token);
+        final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, token);
+        token.addWindow(window1);
+        assertEquals(window1, token.findMainWindow());
+        window1.mAnimatingExit = true;
+        assertEquals(window1, token.findMainWindow());
+        final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, token);
+        token.addWindow(window2);
+        assertEquals(window2, token.findMainWindow());
+    }
+
+    private WindowState createWindow(WindowState parent, int type, WindowToken token) {
+        final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(type);
+
+        return new WindowState(sWm, null, mIWindow, token, parent, 0, 0, attrs, 0,
+                sWm.getDefaultDisplayContentLocked(), 0);
+    }
+
+    /* Used so we can gain access to some protected members of the {@link AppWindowToken} class */
+    private class TestAppWindowToken extends AppWindowToken {
+
+        TestAppWindowToken() {
+            super(sWm, null, false);
+        }
+
+        int getWindowsCount() {
+            return mChildren.size();
+        }
+
+        boolean hasWindow(WindowState w) {
+            return mChildren.contains(w);
+        }
+    }
+}
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 bd7c0b3..ea8b7bb 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
@@ -20,33 +20,35 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import java.util.Comparator;
+import java.util.LinkedList;
 
 /**
  * Test class for {@link WindowContainer}.
  *
  * Build: mmma -j32 frameworks/base/services/tests/servicestests
- * Install: adb install -r out/target/product/marlin/data/app/FrameworksServicesTests/FrameworksServicesTests.apk
+ * Install: adb install -r out/target/product/$TARGET_PRODUCT/data/app/FrameworksServicesTests/FrameworksServicesTests.apk
  * Run: adb shell am instrument -w -e class com.android.server.wm.WindowContainerTests com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
  */
 @SmallTest
 public class WindowContainerTests extends AndroidTestCase {
 
     public void testCreation() throws Exception {
-        final TestWindowContainer w = new TestWindowContainer();
+        final TestWindowContainer w = new TestWindowContainerBuilder().setLayer(0).build();
         assertNull("window must have no parent", w.getParentWindow());
         assertEquals("window must have no children", 0, w.getChildrenCount());
     }
 
     public void testAdd() throws Exception {
-        final TestWindowContainer root = new TestWindowContainer();
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+        final TestWindowContainer root = builder.setLayer(0).build();
 
-        final TestWindowContainer layer1 = root.addChildWindow(1);
-        final TestWindowContainer secondLayer1 = root.addChildWindow(1);
-        final TestWindowContainer layer2 = root.addChildWindow(2);
-        final TestWindowContainer layerNeg1 = root.addChildWindow(-1);
-        final TestWindowContainer layerNeg2 = root.addChildWindow(-2);
-        final TestWindowContainer secondLayerNeg1 = root.addChildWindow(-1);
-        final TestWindowContainer layer0 = root.addChildWindow(0);
+        final TestWindowContainer layer1 = root.addChildWindow(builder.setLayer(1));
+        final TestWindowContainer secondLayer1 = root.addChildWindow(builder.setLayer(1));
+        final TestWindowContainer layer2 = root.addChildWindow(builder.setLayer(2));
+        final TestWindowContainer layerNeg1 = root.addChildWindow(builder.setLayer(-1));
+        final TestWindowContainer layerNeg2 = root.addChildWindow(builder.setLayer(-2));
+        final TestWindowContainer secondLayerNeg1 = root.addChildWindow(builder.setLayer(-1));
+        final TestWindowContainer layer0 = root.addChildWindow(builder.setLayer(0));
 
         assertEquals(7, root.getChildrenCount());
 
@@ -68,13 +70,14 @@
     }
 
     public void testHasChild() throws Exception {
-        final TestWindowContainer root = new TestWindowContainer();
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+        final TestWindowContainer root = builder.setLayer(0).build();
 
-        final TestWindowContainer child1 = root.addChildWindow(1);
-        final TestWindowContainer child2 = root.addChildWindow(1);
-        final TestWindowContainer child11 = child1.addChildWindow(1);
-        final TestWindowContainer child12 = child1.addChildWindow(1);
-        final TestWindowContainer child21 = child2.addChildWindow(1);
+        final TestWindowContainer child1 = root.addChildWindow();
+        final TestWindowContainer child2 = root.addChildWindow();
+        final TestWindowContainer child11 = child1.addChildWindow();
+        final TestWindowContainer child12 = child1.addChildWindow();
+        final TestWindowContainer child21 = child2.addChildWindow();
 
         assertEquals(2, root.getChildrenCount());
         assertEquals(2, child1.getChildrenCount());
@@ -96,13 +99,14 @@
    }
 
     public void testRemove() throws Exception {
-        final TestWindowContainer root = new TestWindowContainer();
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+        final TestWindowContainer root = builder.setLayer(0).build();
 
-        final TestWindowContainer child1 = root.addChildWindow(1);
-        final TestWindowContainer child2 = root.addChildWindow(1);
-        final TestWindowContainer child11 = child1.addChildWindow(1);
-        final TestWindowContainer child12 = child1.addChildWindow(1);
-        final TestWindowContainer child21 = child2.addChildWindow(1);
+        final TestWindowContainer child1 = root.addChildWindow();
+        final TestWindowContainer child2 = root.addChildWindow();
+        final TestWindowContainer child11 = child1.addChildWindow();
+        final TestWindowContainer child12 = child1.addChildWindow();
+        final TestWindowContainer child21 = child2.addChildWindow();
 
         child12.remove();
         assertNull(child12.getParentWindow());
@@ -125,9 +129,67 @@
         assertEquals(0, root.getChildrenCount());
     }
 
+    public void testDetachFromDisplay() throws Exception {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+        final TestWindowContainer root = builder.setLayer(0).build();
+
+        final TestWindowContainer child1 = root.addChildWindow(builder.setCanDetach(true));
+        final TestWindowContainer child2 = root.addChildWindow();
+        final TestWindowContainer child11 = child1.addChildWindow();
+        final TestWindowContainer child12 = child1.addChildWindow(builder.setCanDetach(true));
+        final TestWindowContainer child21 = child2.addChildWindow();
+
+        assertTrue(root.detachFromDisplay());
+        assertTrue(child1.detachFromDisplay());
+        assertFalse(child11.detachFromDisplay());
+        assertTrue(child12.detachFromDisplay());
+        assertFalse(child2.detachFromDisplay());
+        assertFalse(child21.detachFromDisplay());
+    }
+
+    public void testIsAnimating() throws Exception {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+        final TestWindowContainer root = builder.setLayer(0).build();
+
+        final TestWindowContainer child1 = root.addChildWindow(builder.setIsAnimating(true));
+        final TestWindowContainer child2 = root.addChildWindow();
+        final TestWindowContainer child11 = child1.addChildWindow();
+        final TestWindowContainer child12 = child1.addChildWindow(builder.setIsAnimating(true));
+        final TestWindowContainer child21 = child2.addChildWindow();
+
+        assertTrue(root.isAnimating());
+        assertTrue(child1.isAnimating());
+        assertFalse(child11.isAnimating());
+        assertTrue(child12.isAnimating());
+        assertFalse(child2.isAnimating());
+        assertFalse(child21.isAnimating());
+    }
+
+    public void testIsVisible() throws Exception {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+        final TestWindowContainer root = builder.setLayer(0).build();
+
+        final TestWindowContainer child1 = root.addChildWindow(builder.setIsVisible(true));
+        final TestWindowContainer child2 = root.addChildWindow();
+        final TestWindowContainer child11 = child1.addChildWindow();
+        final TestWindowContainer child12 = child1.addChildWindow(builder.setIsVisible(true));
+        final TestWindowContainer child21 = child2.addChildWindow();
+
+        assertTrue(root.isVisible());
+        assertTrue(child1.isVisible());
+        assertFalse(child11.isVisible());
+        assertTrue(child12.isVisible());
+        assertFalse(child2.isVisible());
+        assertFalse(child21.isVisible());
+    }
+
     /* Used so we can gain access to some protected members of the {@link WindowContainer} class */
     private class TestWindowContainer extends WindowContainer {
         private final int mLayer;
+        private final LinkedList<String> mUsers = new LinkedList();
+        private final boolean mCanDetach;
+        private boolean mIsAnimating;
+        private boolean mIsVisible;
 
         /**
          * Compares 2 window layers and returns -1 if the first is lesser than the second in terms
@@ -145,12 +207,13 @@
             return 1;
         };
 
-        TestWindowContainer() {
-            mLayer = 0;
-        }
-
-        TestWindowContainer(int layer) {
+        TestWindowContainer(int layer, LinkedList<String> users, boolean canDetach,
+                boolean isAnimating, boolean isVisible) {
             mLayer = layer;
+            mUsers.addAll(users);
+            mCanDetach = canDetach;
+            mIsAnimating = isAnimating;
+            mIsVisible = isVisible;
         }
 
         TestWindowContainer getParentWindow() {
@@ -161,15 +224,79 @@
             return mChildren.size();
         }
 
-        TestWindowContainer addChildWindow(int layer) {
-            TestWindowContainer child = new TestWindowContainer(layer);
+        TestWindowContainer addChildWindow(TestWindowContainerBuilder childBuilder) {
+            TestWindowContainer child = childBuilder.build();
             addChild(child, mWindowSubLayerComparator);
             return child;
         }
 
+        TestWindowContainer addChildWindow() {
+            return addChildWindow(new TestWindowContainerBuilder().setLayer(1));
+        }
+
         TestWindowContainer getChildAt(int index) {
             return (TestWindowContainer) mChildren.get(index);
         }
+
+        @Override
+        boolean detachFromDisplay() {
+            return super.detachFromDisplay() || mCanDetach;
+        }
+
+        @Override
+        boolean isAnimating() {
+            return mIsAnimating || super.isAnimating();
+        }
+
+        @Override
+        boolean isVisible() {
+            return mIsVisible || super.isVisible();
+        }
     }
 
+    private class TestWindowContainerBuilder {
+        private int mLayer;
+        private LinkedList<String> mUsers = new LinkedList();
+        private boolean mCanDetach;
+        private boolean mIsAnimating;
+        private boolean mIsVisible;
+
+        TestWindowContainerBuilder setLayer(int layer) {
+            mLayer = layer;
+            return this;
+        }
+
+        TestWindowContainerBuilder addUser(String user) {
+            mUsers.add(user);
+            return this;
+        }
+
+        TestWindowContainerBuilder setCanDetach(boolean canDetach) {
+            mCanDetach = canDetach;
+            return this;
+        }
+
+        TestWindowContainerBuilder setIsAnimating(boolean isAnimating) {
+            mIsAnimating = isAnimating;
+            return this;
+        }
+
+        TestWindowContainerBuilder setIsVisible(boolean isVisible) {
+            mIsVisible = isVisible;
+            return this;
+        }
+
+        TestWindowContainerBuilder reset() {
+            mLayer = 0;
+            mUsers.clear();
+            mCanDetach = false;
+            mIsAnimating = false;
+            mIsVisible = false;
+            return this;
+        }
+
+        TestWindowContainer build() {
+            return new TestWindowContainer(mLayer, mUsers, mCanDetach, mIsAnimating, mIsVisible);
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
index 64de15d..c55cfb9 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
@@ -32,7 +32,7 @@
  * Tests for the {@link WindowState} class.
  *
  * Build: mmma -j32 frameworks/base/services/tests/servicestests
- * Install: adb install -r out/target/product/angler/data/app/FrameworksServicesTests/FrameworksServicesTests.apk
+ * Install: adb install -r out/target/product/$TARGET_PRODUCT/data/app/FrameworksServicesTests/FrameworksServicesTests.apk
  * Run: adb shell am instrument -w -e class com.android.server.wm.WindowStateTests com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
  */
 @SmallTest
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
new file mode 100644
index 0000000..b907161
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
@@ -0,0 +1,172 @@
+/*
+ * 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 android.content.Context;
+import android.os.IBinder;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.IWindow;
+import android.view.WindowManager;
+import android.view.WindowManagerPolicy;
+
+import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
+
+/**
+ * Tests for the {@link WindowState} class.
+ *
+ * Build: mmma -j32 frameworks/base/services/tests/servicestests
+ * Install: adb install -r out/target/product/$TARGET_PRODUCT/data/app/FrameworksServicesTests/FrameworksServicesTests.apk
+ * Run: adb shell am instrument -w -e class com.android.server.wm.WindowTokenTests com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
+ */
+@SmallTest
+public class WindowTokenTests extends AndroidTestCase {
+
+    private static WindowManagerService sWm = null;
+    private final WindowManagerPolicy mPolicy = new TestWindowManagerPolicy();
+    private final IWindow mIWindow = new TestIWindow();
+
+    @Override
+    public void setUp() throws Exception {
+        final Context context = getContext();
+        if (sWm == null) {
+            // We only want to do this once for the test process as we don't want WM to try to
+            // register a bunch of local services again.
+            sWm = WindowManagerService.main(context, null, true, false, false, mPolicy);
+        }
+    }
+
+    public void testAddWindow() throws Exception {
+        final TestWindowToken token = new TestWindowToken();
+
+        assertEquals(0, token.getWindowsCount());
+
+        final WindowState window1 = createWindow(null, TYPE_APPLICATION, token);
+        final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, token);
+        final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, token);
+        final WindowState window2 = createWindow(null, TYPE_APPLICATION, token);
+        final WindowState window3 = createWindow(null, TYPE_APPLICATION, token);
+
+        token.addWindow(window1);
+        // NOTE: Child windows will not be added to the token as window containers can only
+        // contain/reference their direct children.
+        token.addWindow(window11);
+        token.addWindow(window12);
+        token.addWindow(window2);
+        token.addWindow(window3);
+
+        // Should not contain the child windows that were added above.
+        assertEquals(3, token.getWindowsCount());
+        assertTrue(token.hasWindow(window1));
+        assertFalse(token.hasWindow(window11));
+        assertFalse(token.hasWindow(window12));
+        assertTrue(token.hasWindow(window2));
+        assertTrue(token.hasWindow(window3));
+    }
+
+    public void testAdjustAnimLayer() throws Exception {
+        final TestWindowToken token = new TestWindowToken();
+        final WindowState window1 = createWindow(null, TYPE_APPLICATION, token);
+        final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, token);
+        final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, token);
+        final WindowState window2 = createWindow(null, TYPE_APPLICATION, token);
+        final WindowState window3 = createWindow(null, TYPE_APPLICATION, token);
+
+        token.addWindow(window1);
+        token.addWindow(window2);
+        token.addWindow(window3);
+
+        final int adj = 50;
+        final int window2StartLayer = window2.mLayer = 100;
+        final int window3StartLayer = window3.mLayer = 200;
+        final int highestLayer = token.adjustAnimLayer(adj);
+
+        assertEquals(adj, window1.mWinAnimator.mAnimLayer);
+        assertEquals(adj, window11.mWinAnimator.mAnimLayer);
+        assertEquals(adj, window12.mWinAnimator.mAnimLayer);
+        assertEquals(window2StartLayer + adj, window2.mWinAnimator.mAnimLayer);
+        assertEquals(window3StartLayer + adj, window3.mWinAnimator.mAnimLayer);
+        assertEquals(window3StartLayer + adj, highestLayer);
+    }
+
+    public void testGetTopWindow() throws Exception {
+        final TestWindowToken token = new TestWindowToken();
+
+        assertNull(token.getTopWindow());
+
+        final WindowState window1 = createWindow(null, TYPE_APPLICATION, token);
+        token.addWindow(window1);
+        assertEquals(window1, token.getTopWindow());
+        final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, token);
+        final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, token);
+        assertEquals(window12, token.getTopWindow());
+
+        final WindowState window2 = createWindow(null, TYPE_APPLICATION, token);
+        token.addWindow(window2);
+        // Since new windows are added to the bottom of the token, we would still expect the
+        // previous one to the top.
+        assertEquals(window12, token.getTopWindow());
+    }
+
+    public void testGetWindowIndex() throws Exception {
+        final TestWindowToken token = new TestWindowToken();
+
+        final WindowState window1 = createWindow(null, TYPE_APPLICATION, token);
+        assertEquals(-1, token.getWindowIndex(window1));
+        token.addWindow(window1);
+        assertEquals(0, token.getWindowIndex(window1));
+        final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, token);
+        final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, token);
+        // Child windows should report the same index as their parents.
+        assertEquals(0, token.getWindowIndex(window11));
+        assertEquals(0, token.getWindowIndex(window12));
+
+        final WindowState window2 = createWindow(null, TYPE_APPLICATION, token);
+        assertEquals(-1, token.getWindowIndex(window2));
+        token.addWindow(window2);
+        // Since new windows are added to the bottom of the token, we would expect the added window
+        // to be at index 0.
+        assertEquals(0, token.getWindowIndex(window2));
+        assertEquals(1, token.getWindowIndex(window1));
+    }
+
+    private WindowState createWindow(WindowState parent, int type, WindowToken token) {
+        final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(type);
+
+        return new WindowState(sWm, null, mIWindow, token, parent, 0, 0, attrs, 0,
+                sWm.getDefaultDisplayContentLocked(), 0);
+    }
+
+    /* Used so we can gain access to some protected members of the {@link WindowToken} class */
+    private class TestWindowToken extends WindowToken {
+
+        TestWindowToken() {
+            super(sWm, null, 0, false);
+        }
+
+        int getWindowsCount() {
+            return mChildren.size();
+        }
+
+        boolean hasWindow(WindowState w) {
+            return mChildren.contains(w);
+        }
+    }
+}