Made WindowState.mChildWindow private scoped

This involved:
- Moving method that operated on mChildWindows and mostly touched
WindowState fields into WindowState class.
- Creating new methods for getting information about child windows
outside WindowState class instead of accessing the child list directly.
- And, tests ;)

Bug: 30060889
Change-Id: I339cecb926b44b5db7ead1970e356304d5429c8f
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 894c27c..1d6cc32 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1206,33 +1206,10 @@
                     (int) windowFrame.right, (int) windowFrame.bottom);
         }
 
-        private static WindowInfo obtainPopulatedWindowInfo(WindowState windowState,
-                Rect boundsInScreen) {
-            WindowInfo window = WindowInfo.obtain();
-            window.type = windowState.mAttrs.type;
-            window.layer = windowState.mLayer;
-            window.token = windowState.mClient.asBinder();
-            window.title = windowState.mAttrs.accessibilityTitle;
-            window.accessibilityIdOfAnchor = windowState.mAttrs.accessibilityIdOfAnchor;
-
-            if (windowState.isChildWindow()) {
-                window.parentToken = windowState.mParentWindow.mClient.asBinder();
-            }
-
-            window.focused = windowState.isFocused();
+        private static WindowInfo obtainPopulatedWindowInfo(
+                WindowState windowState, Rect boundsInScreen) {
+            final WindowInfo window = windowState.getWindowInfo();
             window.boundsInScreen.set(boundsInScreen);
-
-            final int childCount = windowState.mChildWindows.size();
-            if (childCount > 0) {
-                if (window.childTokens == null) {
-                    window.childTokens = new ArrayList<IBinder>();
-                }
-                for (int j = 0; j < childCount; j++) {
-                    WindowState child = windowState.mChildWindows.get(j);
-                    window.childTokens.add(child.mClient.asBinder());
-                }
-            }
-
             return window;
         }
 
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index 2c0bb92..39a549d 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -424,7 +424,7 @@
 
         final int numAllAppWinAnimators = mAllAppWinAnimators.size();
         for (int i = 0; i < numAllAppWinAnimators; i++) {
-            mAllAppWinAnimators.get(i).finishExit();
+            mAllAppWinAnimators.get(i).mWin.onExitAnimationDone();
         }
         mService.mAppTransition.notifyAppTransitionFinishedLocked(mAppToken.token);
         return false;
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index e464ffa..05ce7ae 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -362,7 +362,7 @@
 
             win.destroyOrSaveSurface();
             if (win.mRemoveOnExit) {
-                service.removeWindowInnerLocked(win);
+                win.removeLocked();
             }
             final DisplayContent displayContent = win.getDisplayContent();
             if (displayContent != null && !displayList.contains(displayContent)) {
@@ -680,10 +680,10 @@
                 candidate.mReplacingWindow.mSkipEnterAnimationForSeamlessReplacement = false;
             }
             // Since the window already timed out, remove it immediately now.
-            // Use removeWindowInnerLocked() instead of removeWindowLocked(), as the latter
+            // Use WindowState#removeLocked() instead of removeWindowLocked(), as the latter
             // delays removal on certain conditions, which will leave the stale window in the
             // stack and marked mWillReplaceWindow=false, so the window will never be removed.
-            service.removeWindowInnerLocked(candidate);
+            candidate.removeLocked();
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowLayersController.java b/services/core/java/com/android/server/wm/WindowLayersController.java
index 072e10f..7405cf9 100644
--- a/services/core/java/com/android/server/wm/WindowLayersController.java
+++ b/services/core/java/com/android/server/wm/WindowLayersController.java
@@ -119,18 +119,13 @@
         mInputMethodAnimLayerAdjustment = adj;
         final WindowState imw = mService.mInputMethodWindow;
         if (imw != null) {
-            imw.mWinAnimator.mAnimLayer = imw.mLayer + adj;
-            if (DEBUG_LAYERS) Slog.v(TAG_WM, "IM win " + imw
-                    + " anim layer: " + imw.mWinAnimator.mAnimLayer);
-            for (int i = imw.mChildWindows.size() - 1; i >= 0; i--) {
-                final WindowState childWindow = imw.mChildWindows.get(i);
-                childWindow.mWinAnimator.mAnimLayer = childWindow.mLayer + adj;
-                if (DEBUG_LAYERS) Slog.v(TAG_WM, "IM win " + childWindow
-                        + " anim layer: " + childWindow.mWinAnimator.mAnimLayer);
-            }
+            imw.adjustAnimLayer(adj);
         }
         for (int i = mService.mInputMethodDialogs.size() - 1; i >= 0; i--) {
             final WindowState dialog = mService.mInputMethodDialogs.get(i);
+            // TODO: This and other places setting mAnimLayer can probably use WS.adjustAnimLayer,
+            // but need to make sure we are not setting things twice for child windows that are
+            // already in the list.
             dialog.mWinAnimator.mAnimLayer = dialog.mLayer + adj;
             if (DEBUG_LAYERS) Slog.v(TAG_WM, "IM win " + imw
                     + " anim layer: " + dialog.mWinAnimator.mAnimLayer);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 9c7e0f8..76189be 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1146,14 +1146,9 @@
     private int indexOfWinInWindowList(WindowState targetWin, WindowList windows) {
         for (int i = windows.size() - 1; i >= 0; i--) {
             final WindowState w = windows.get(i);
-            if (w == targetWin) {
+            if (w == targetWin || w.hasChild(targetWin)) {
                 return i;
             }
-            if (!w.mChildWindows.isEmpty()) {
-                if (indexOfWinInWindowList(targetWin, w.mChildWindows) >= 0) {
-                    return i;
-                }
-            }
         }
         return -1;
     }
@@ -1646,30 +1641,6 @@
         moveInputMethodDialogsLocked(pos);
     }
 
-    private int tmpRemoveWindowLocked(int interestingPos, WindowState win) {
-        WindowList windows = win.getWindowList();
-        int wpos = windows.indexOf(win);
-        if (wpos >= 0) {
-            if (wpos < interestingPos) interestingPos--;
-            if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Temp removing at " + wpos + ": " + win);
-            windows.remove(wpos);
-            mWindowsChanged = true;
-            int NC = win.mChildWindows.size();
-            while (NC > 0) {
-                NC--;
-                WindowState cw = win.mChildWindows.get(NC);
-                int cpos = windows.indexOf(cw);
-                if (cpos >= 0) {
-                    if (cpos < interestingPos) interestingPos--;
-                    if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Temp removing child at "
-                            + cpos + ": " + cw);
-                    windows.remove(cpos);
-                }
-            }
-        }
-        return interestingPos;
-    }
-
     private void reAddWindowToListInOrderLocked(WindowState win) {
         addWindowToListInOrderLocked(win, false);
         // This is a hack to get all of the child windows added as well
@@ -1681,7 +1652,7 @@
             if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "ReAdd removing from " + wpos + ": " + win);
             windows.remove(wpos);
             mWindowsChanged = true;
-            reAddWindowLocked(wpos, win);
+            win.reAddWindowLocked(wpos);
         }
     }
 
@@ -1701,7 +1672,7 @@
         final int N = dialogs.size();
         if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, "Removing " + N + " dialogs w/pos=" + pos);
         for (int i=0; i<N; i++) {
-            pos = tmpRemoveWindowLocked(pos, dialogs.get(i));
+            pos = dialogs.get(i).removeFromWindowList(pos);
         }
         if (DEBUG_INPUT_METHOD) {
             Slog.v(TAG_WM, "Window list w/pos=" + pos);
@@ -1725,7 +1696,7 @@
             for (int i=0; i<N; i++) {
                 WindowState win = dialogs.get(i);
                 win.mTargetAppToken = targetAppToken;
-                pos = reAddWindowLocked(pos, win);
+                pos = win.reAddWindowLocked(pos);
             }
             if (DEBUG_INPUT_METHOD) {
                 Slog.v(TAG_WM, "Final window list:");
@@ -1762,16 +1733,14 @@
             // First check to see if the input method windows are already
             // located here, and contiguous.
             final int N = windows.size();
-            WindowState firstImWin = imPos < N
-                    ? windows.get(imPos) : null;
+            final WindowState firstImWin = imPos < N ? windows.get(imPos) : null;
 
             // Figure out the actual input method window that should be
             // at the bottom of their stack.
-            WindowState baseImWin = imWin != null
-                    ? imWin : mInputMethodDialogs.get(0);
-            if (baseImWin.mChildWindows.size() > 0) {
-                WindowState cw = baseImWin.mChildWindows.get(0);
-                if (cw.mSubLayer < 0) baseImWin = cw;
+            WindowState baseImWin = imWin != null ? imWin : mInputMethodDialogs.get(0);
+            final WindowState cw = baseImWin.getBottomChild();
+            if (cw != null && cw.mSubLayer < 0) {
+                baseImWin = cw;
             }
 
             if (firstImWin == baseImWin) {
@@ -1807,13 +1776,13 @@
                     Slog.v(TAG_WM, "Moving IM from " + imPos);
                     logWindowList(windows, "  ");
                 }
-                imPos = tmpRemoveWindowLocked(imPos, imWin);
+                imPos = imWin.removeFromWindowList(imPos);
                 if (DEBUG_INPUT_METHOD) {
                     Slog.v(TAG_WM, "List after removing with new pos " + imPos + ":");
                     logWindowList(windows, "  ");
                 }
                 imWin.mTargetAppToken = mInputMethodTarget.mAppToken;
-                reAddWindowLocked(imPos, imWin);
+                imWin.reAddWindowLocked(imPos);
                 if (DEBUG_INPUT_METHOD) {
                     Slog.v(TAG_WM, "List after moving IM to " + imPos + ":");
                     logWindowList(windows, "  ");
@@ -1829,7 +1798,7 @@
 
             if (imWin != null) {
                 if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, "Moving IM from " + imPos);
-                tmpRemoveWindowLocked(0, imWin);
+                imWin.removeFromWindowList(0);
                 imWin.mTargetAppToken = null;
                 reAddWindowToListInOrderLocked(imWin);
                 if (DEBUG_INPUT_METHOD) {
@@ -1850,7 +1819,7 @@
         return true;
     }
 
-    private static boolean excludeWindowTypeFromTapOutTask(int windowType) {
+    static boolean excludeWindowTypeFromTapOutTask(int windowType) {
         switch (windowType) {
             case TYPE_STATUS_BAR:
             case TYPE_NAVIGATION_BAR:
@@ -2454,7 +2423,7 @@
             }
         }
 
-        removeWindowInnerLocked(win);
+        win.removeLocked();
         // Removing a visible window will effect the computed orientation
         // So just update orientation if needed.
         if (wasVisible && updateOrientationFromAppTokensLocked(false)) {
@@ -2464,41 +2433,12 @@
         Binder.restoreCallingIdentity(origId);
     }
 
-    void removeWindowInnerLocked(WindowState win) {
-        if (win.mRemoved) {
-            // Nothing to do.
-            if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
-                    "removeWindowInnerLocked: " + win + " Already removed...");
-            return;
-        }
-
-        for (int i = win.mChildWindows.size() - 1; i >= 0; i--) {
-            WindowState cwin = win.mChildWindows.get(i);
-            Slog.w(TAG_WM, "Force-removing child win " + cwin + " from container " + win);
-            removeWindowInnerLocked(cwin);
-        }
-
-        win.mRemoved = true;
-
-        if (mInputMethodTarget == win) {
-            moveInputMethodWindowsIfNeededLocked(false);
-        }
-
-        if (false) {
-            RuntimeException e = new RuntimeException("here");
-            e.fillInStackTrace();
-            Slog.w(TAG_WM, "Removing window " + win, e);
-        }
-
-        final int type = win.mAttrs.type;
-        if (excludeWindowTypeFromTapOutTask(type)) {
-            final DisplayContent displaycontent = win.getDisplayContent();
-            displaycontent.mTapExcludedWindows.remove(win);
-        }
-        mPolicy.removeWindowLw(win);
-        win.removeLocked();
-
-        if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "removeWindowInnerLocked: " + win);
+    /**
+     * Performs some centralized bookkeeping clean-up on the window that is being removed.
+     * NOTE: Should only be called from {@link WindowState#removeLocked()}
+     */
+    void postWindowRemoveCleanupLocked(WindowState win) {
+        if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "postWindowRemoveCleanupLocked: " + win);
         mWindowMap.remove(win.mClient.asBinder());
         if (win.mAppOp != AppOpsManager.OP_NONE) {
             mAppOps.finishOp(win.mAppOp, win.getOwningUid(), win.getOwningPackage());
@@ -2551,7 +2491,7 @@
             }
         }
 
-        if (type == TYPE_WALLPAPER) {
+        if (win.mAttrs.type == TYPE_WALLPAPER) {
             mWallpaperControllerLocked.clearLastWallpaperTimeoutTime();
             getDefaultDisplayContentLocked().pendingLayoutChanges |=
                     WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
@@ -4377,7 +4317,7 @@
                 WindowState win = wtoken.allAppWindows.get(i);
                 if (win == wtoken.startingWindow) {
                     // Starting window that's exiting will be removed when the animation
-                    // finishes. Mark all relevant flags for that finishExit will proceed
+                    // finishes. Mark all relevant flags for that onExitAnimationDone will proceed
                     // all the way to actually remove it.
                     if (!visible && win.isVisibleNow() && wtoken.mAppAnimator.isAnimating()) {
                         win.mAnimatingExit = true;
@@ -4865,38 +4805,6 @@
         }
     }
 
-    private final int reAddWindowLocked(int index, WindowState win) {
-        final WindowList windows = win.getWindowList();
-        // Adding child windows relies on mChildWindows being ordered by mSubLayer.
-        final int NCW = win.mChildWindows.size();
-        boolean winAdded = false;
-        for (int j=0; j<NCW; j++) {
-            WindowState cwin = win.mChildWindows.get(j);
-            if (!winAdded && cwin.mSubLayer >= 0) {
-                if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Re-adding child window at "
-                        + index + ": " + cwin);
-                win.mRebuilding = false;
-                windows.add(index, win);
-                index++;
-                winAdded = true;
-            }
-            if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Re-adding window at "
-                    + index + ": " + cwin);
-            cwin.mRebuilding = false;
-            windows.add(index, cwin);
-            index++;
-        }
-        if (!winAdded) {
-            if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Re-adding window at "
-                    + index + ": " + win);
-            win.mRebuilding = false;
-            windows.add(index, win);
-            index++;
-        }
-        mWindowsChanged = true;
-        return index;
-    }
-
     private final int reAddAppWindowsLocked(final DisplayContent displayContent, int index,
                                             WindowToken token) {
         final int NW = token.windows.size();
@@ -4905,7 +4813,7 @@
             final DisplayContent winDisplayContent = win.getDisplayContent();
             if (winDisplayContent == displayContent || winDisplayContent == null) {
                 win.mDisplayContent = displayContent;
-                index = reAddWindowLocked(index, win);
+                index = win.reAddWindowLocked(index);
             }
         }
         return index;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 5c40947..4dbac7e 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -49,6 +49,7 @@
 import android.view.InputEventReceiver;
 import android.view.View;
 import android.view.ViewTreeObserver;
+import android.view.WindowInfo;
 import android.view.WindowManager;
 import android.view.WindowManagerPolicy;
 
@@ -61,6 +62,7 @@
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
@@ -100,6 +102,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_POWER;
@@ -107,6 +110,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SURFACE_TRACE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowStateAnimator.COMMIT_DRAW_PENDING;
@@ -157,7 +161,7 @@
     final WindowManager.LayoutParams mAttrs = new WindowManager.LayoutParams();
     final DeathRecipient mDeathRecipient;
     final WindowState mParentWindow;
-    final WindowList mChildWindows = new WindowList();
+    private final WindowList mChildWindows = new WindowList();
     final int mBaseLayer;
     final int mSubLayer;
     final boolean mLayoutAttached;
@@ -1454,6 +1458,31 @@
     }
 
     void removeLocked() {
+        if (mRemoved) {
+            // Nothing to do.
+            if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "WS.removeLocked: " + this + " Already removed...");
+            return;
+        }
+
+        for (int i = mChildWindows.size() - 1; i >= 0; i--) {
+            final WindowState child = mChildWindows.get(i);
+            Slog.w(TAG_WM, "Force-removing child win " + child + " from container " + this);
+            child.removeLocked();
+        }
+
+        mRemoved = true;
+
+        if (mService.mInputMethodTarget == this) {
+            mService.moveInputMethodWindowsIfNeededLocked(false);
+        }
+
+        final int type = mAttrs.type;
+        if (WindowManagerService.excludeWindowTypeFromTapOutTask(type)) {
+            final DisplayContent displaycontent = getDisplayContent();
+            displaycontent.mTapExcludedWindows.remove(this);
+        }
+        mPolicy.removeWindowLw(this);
+
         disposeInputChannel();
 
         if (isChildWindow()) {
@@ -1469,6 +1498,8 @@
             // Ignore if it has already been removed (usually because
             // we are doing this as part of processing a death note.)
         }
+
+        mService.postWindowRemoveCleanupLocked(this);
     }
 
     void setHasSurface(boolean hasSurface) {
@@ -1630,7 +1661,7 @@
                 win.mReplacingWindow = null;
                 mSkipEnterAnimationForSeamlessReplacement = false;
                 if (win.mAnimatingExit || !animateReplacingWindow) {
-                    mService.removeWindowInnerLocked(win);
+                    win.removeLocked();
                 }
             }
         }
@@ -2830,6 +2861,24 @@
         return mParentWindow != null;
     }
 
+    boolean hasChild(WindowState child) {
+        for (int i = mChildWindows.size() - 1; i >= 0; --i) {
+            if (mChildWindows.get(i) == child) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns the bottom child window in regards to z-order of this window or null if no children.
+     */
+    WindowState getBottomChild() {
+        // Child windows are z-ordered based on sub-layer using {@link #sWindowSubLayerComparator}
+        // and the child with the lowest z-order will be at the head of the list.
+        return mChildWindows.isEmpty() ? null : mChildWindows.get(0);
+    }
+
     boolean layoutInParentFrame() {
         return isChildWindow() && (mAttrs.privateFlags & PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME) != 0;
     }
@@ -2983,4 +3032,170 @@
                     + " Callers=" + Debug.getCallers(4));
         }
     }
+
+    WindowInfo getWindowInfo() {
+        WindowInfo windowInfo = WindowInfo.obtain();
+        windowInfo.type = mAttrs.type;
+        windowInfo.layer = mLayer;
+        windowInfo.token = mClient.asBinder();
+        windowInfo.title = mAttrs.accessibilityTitle;
+        windowInfo.accessibilityIdOfAnchor = mAttrs.accessibilityIdOfAnchor;
+        windowInfo.focused = isFocused();
+
+        if (isChildWindow()) {
+            windowInfo.parentToken = mParentWindow.mClient.asBinder();
+        }
+
+        final int childCount = mChildWindows.size();
+        if (childCount > 0) {
+            if (windowInfo.childTokens == null) {
+                windowInfo.childTokens = new ArrayList<>();
+            }
+            for (int j = 0; j < childCount; j++) {
+                final WindowState child = mChildWindows.get(j);
+                windowInfo.childTokens.add(child.mClient.asBinder());
+            }
+        }
+
+        return windowInfo;
+    }
+
+    void adjustAnimLayer(int adj) {
+        mWinAnimator.mAnimLayer = mLayer + adj;
+        if (DEBUG_LAYERS) Slog.v(TAG_WM, "win=" + this + " anim layer: " + mWinAnimator.mAnimLayer);
+        for (int i = mChildWindows.size() - 1; i >= 0; i--) {
+            final WindowState child = mChildWindows.get(i);
+            child.adjustAnimLayer(adj);
+        }
+    }
+
+    // TODO: come-up with a better name for this method that represents what it does.
+    // Or, it is probably not going to matter anyways if we are successful in getting rid of
+    // the WindowList concept.
+    int reAddWindowLocked(int index) {
+        final WindowList windows = getWindowList();
+        // Adding child windows relies on child windows being ordered by mSubLayer using
+        // {@link #sWindowSubLayerComparator}.
+        final int childCount = mChildWindows.size();
+        boolean winAdded = false;
+        for (int j = 0; j < childCount; j++) {
+            WindowState child = mChildWindows.get(j);
+            if (!winAdded && child.mSubLayer >= 0) {
+                if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM,
+                        "Re-adding child window at " + index + ": " + child);
+                mRebuilding = false;
+                windows.add(index, this);
+                index++;
+                winAdded = true;
+            }
+            if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Re-adding window at " + index + ": " + child);
+            child.mRebuilding = false;
+            windows.add(index, child);
+            index++;
+        }
+        if (!winAdded) {
+            if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Re-adding window at " + index + ": " + this);
+            mRebuilding = false;
+            windows.add(index, this);
+            index++;
+        }
+        mService.mWindowsChanged = true;
+        return index;
+    }
+
+    int removeFromWindowList(int interestingPos) {
+        final WindowList windows = getWindowList();
+        int wpos = windows.indexOf(this);
+        if (wpos < 0) {
+            return interestingPos;
+        }
+
+        if (wpos < interestingPos) interestingPos--;
+        if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM, "Temp removing at " + wpos + ": " + this);
+        windows.remove(wpos);
+        mService.mWindowsChanged = true;
+        int childCount = mChildWindows.size();
+        while (childCount > 0) {
+            childCount--;
+            final WindowState cw = mChildWindows.get(childCount);
+            int cpos = windows.indexOf(cw);
+            if (cpos >= 0) {
+                if (cpos < interestingPos) interestingPos--;
+                if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG_WM,
+                        "Temp removing child at " + cpos + ": " + cw);
+                windows.remove(cpos);
+            }
+        }
+        return interestingPos;
+    }
+
+    void onExitAnimationDone() {
+        if (DEBUG_ANIM) Slog.v(TAG, "onExitAnimationDone in " + this
+                + ": exiting=" + mAnimatingExit + " remove=" + mRemoveOnExit
+                + " windowAnimating=" + mWinAnimator.isWindowAnimationSet());
+
+        if (!mChildWindows.isEmpty()) {
+            // Copying to a different list as multiple children can be removed.
+            final WindowList childWindows = new WindowList(mChildWindows);
+            for (int i = childWindows.size() - 1; i >= 0; i--) {
+                childWindows.get(i).onExitAnimationDone();
+            }
+        }
+
+        if (mWinAnimator.mEnteringAnimation) {
+            mWinAnimator.mEnteringAnimation = false;
+            mService.requestTraversal();
+            // System windows don't have an activity and an app token as a result, but need a way
+            // to be informed about their entrance animation end.
+            if (mAppToken == null) {
+                try {
+                    mClient.dispatchWindowShown();
+                } catch (RemoteException e) {
+                }
+            }
+        }
+
+        if (!mWinAnimator.isWindowAnimationSet()) {
+            //TODO (multidisplay): Accessibility is supported only for the default display.
+            if (mService.mAccessibilityController != null && getDisplayId() == DEFAULT_DISPLAY) {
+                mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
+            }
+        }
+
+        if (!mAnimatingExit) {
+            return;
+        }
+
+        if (mWinAnimator.isWindowAnimationSet()) {
+            return;
+        }
+
+        if (WindowManagerService.localLOGV || DEBUG_ADD_REMOVE) Slog.v(TAG,
+                "Exit animation finished in " + this + ": remove=" + mRemoveOnExit);
+
+        mDestroying = true;
+
+        final boolean hasSurface = mWinAnimator.hasSurface();
+        if (hasSurface) {
+            mWinAnimator.hide("onExitAnimationDone");
+        }
+
+        // If we have an app token, we ask it to destroy the surface for us, so that it can take
+        // care to ensure the activity has actually stopped and the surface is not still in use.
+        // Otherwise we add the service to mDestroySurface and allow it to be processed in our next
+        // transaction.
+        if (mAppToken != null) {
+            mAppToken.destroySurfaces();
+        } else {
+            if (hasSurface) {
+                mService.mDestroySurface.add(this);
+            }
+            if (mRemoveOnExit) {
+                mService.mPendingRemove.add(this);
+                mRemoveOnExit = false;
+            }
+        }
+        mAnimatingExit = false;
+        mService.mWallpaperControllerLocked.hideWallpapers(this);
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 9d3a221..673d28c 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -24,7 +24,6 @@
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static com.android.server.wm.AppWindowAnimator.sDummyAnimation;
 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
@@ -53,7 +52,6 @@
 import android.graphics.RectF;
 import android.graphics.Region;
 import android.os.Debug;
-import android.os.RemoteException;
 import android.os.Trace;
 import android.util.Slog;
 import android.view.DisplayInfo;
@@ -102,7 +100,7 @@
     // Unchanging local convenience fields.
     final WindowManagerService mService;
     final WindowState mWin;
-    final WindowStateAnimator mAttachedWinAnimator;
+    private final WindowStateAnimator mParentWinAnimator;
     final WindowAnimator mAnimator;
     AppWindowAnimator mAppAnimator;
     final Session mSession;
@@ -260,8 +258,7 @@
         }
 
         mWin = win;
-        mAttachedWinAnimator = !win.isChildWindow()
-                ? null : win.mParentWindow.mWinAnimator;
+        mParentWinAnimator = !win.isChildWindow() ? null : win.mParentWindow.mWinAnimator;
         mAppAnimator = win.mAppToken == null ? null : win.mAppToken.mAppAnimator;
         mSession = win.mSession;
         mAttrType = win.mAttrs.type;
@@ -309,7 +306,7 @@
      */
     boolean isAnimationSet() {
         return mAnimation != null
-                || (mAttachedWinAnimator != null && mAttachedWinAnimator.mAnimation != null)
+                || (mParentWinAnimator != null && mParentWinAnimator.mAnimation != null)
                 || (mAppAnimator != null && mAppAnimator.isAnimating());
     }
 
@@ -490,7 +487,7 @@
             }
         }
 
-        finishExit();
+        mWin.onExitAnimationDone();
         final int displayId = mWin.getDisplayId();
         mAnimator.setPendingLayoutChanges(displayId, WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM);
         if (DEBUG_LAYOUT_REPEATS)
@@ -504,80 +501,6 @@
         return false;
     }
 
-    void finishExit() {
-        if (DEBUG_ANIM) Slog.v(
-                TAG, "finishExit in " + this
-                + ": exiting=" + mWin.mAnimatingExit
-                + " remove=" + mWin.mRemoveOnExit
-                + " windowAnimating=" + isWindowAnimationSet());
-
-        if (!mWin.mChildWindows.isEmpty()) {
-            // Copying to a different list as multiple children can be removed.
-            final WindowList childWindows = new WindowList(mWin.mChildWindows);
-            for (int i = childWindows.size() - 1; i >= 0; i--) {
-                childWindows.get(i).mWinAnimator.finishExit();
-            }
-        }
-
-        if (mEnteringAnimation) {
-            mEnteringAnimation = false;
-            mService.requestTraversal();
-            // System windows don't have an activity and an app token as a result, but need a way
-            // to be informed about their entrance animation end.
-            if (mWin.mAppToken == null) {
-                try {
-                    mWin.mClient.dispatchWindowShown();
-                } catch (RemoteException e) {
-                }
-            }
-        }
-
-        if (!isWindowAnimationSet()) {
-            //TODO (multidisplay): Accessibility is supported only for the default display.
-            if (mService.mAccessibilityController != null
-                    && mWin.getDisplayId() == DEFAULT_DISPLAY) {
-                mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
-            }
-        }
-
-        if (!mWin.mAnimatingExit) {
-            return;
-        }
-
-        if (isWindowAnimationSet()) {
-            return;
-        }
-
-        if (WindowManagerService.localLOGV || DEBUG_ADD_REMOVE) Slog.v(TAG,
-                "Exit animation finished in " + this + ": remove=" + mWin.mRemoveOnExit);
-
-
-        mWin.mDestroying = true;
-
-        final boolean hasSurface = hasSurface();
-        if (hasSurface) {
-            hide("finishExit");
-        }
-
-        // If we have an app token, we ask it to destroy the surface for us,
-        // so that it can take care to ensure the activity has actually stopped
-        // and the surface is not still in use. Otherwise we add the service to
-        // mDestroySurface and allow it to be processed in our next transaction.
-        if (mWin.mAppToken != null) {
-            mWin.mAppToken.destroySurfaces();
-        } else {
-            if (hasSurface) {
-                mService.mDestroySurface.add(mWin);
-            }
-            if (mWin.mRemoveOnExit) {
-                mService.mPendingRemove.add(mWin);
-                mWin.mRemoveOnExit = false;
-            }
-        }
-        mWin.mAnimatingExit = false;
-        mWallpaperControllerLocked.hideWallpapers(mWin);
-    }
-
     void hide(String reason) {
         if (!mLastHidden) {
             //dump();
@@ -925,8 +848,8 @@
     void computeShownFrameLocked() {
         final boolean selfTransformation = mHasLocalTransformation;
         Transformation attachedTransformation =
-                (mAttachedWinAnimator != null && mAttachedWinAnimator.mHasLocalTransformation)
-                ? mAttachedWinAnimator.mTransformation : null;
+                (mParentWinAnimator != null && mParentWinAnimator.mHasLocalTransformation)
+                ? mParentWinAnimator.mTransformation : null;
         Transformation appTransformation = (mAppAnimator != null && mAppAnimator.hasTransformation)
                 ? mAppAnimator.transformation : null;
 
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 7cf08d2..1b9eeb0 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -213,9 +213,9 @@
             recoveringMemory = true;
             // Wait a little bit for things to settle down, and off we go.
             while (!mService.mForceRemoves.isEmpty()) {
-                WindowState ws = mService.mForceRemoves.remove(0);
+                final WindowState ws = mService.mForceRemoves.remove(0);
                 Slog.i(TAG, "Force removing: " + ws);
-                mService.removeWindowInnerLocked(ws);
+                ws.removeLocked();
             }
             Slog.w(TAG, "Due to memory failure, waiting a bit for next layout");
             Object tmp = new Object();
@@ -544,8 +544,8 @@
             mService.mPendingRemove.clear();
             DisplayContentList displayList = new DisplayContentList();
             for (i = 0; i < N; i++) {
-                WindowState w = mService.mPendingRemoveTmp[i];
-                mService.removeWindowInnerLocked(w);
+                final WindowState w = mService.mPendingRemoveTmp[i];
+                w.removeLocked();
                 final DisplayContent displayContent = w.getDisplayContent();
                 if (displayContent != null && !displayList.contains(displayContent)) {
                     displayList.add(displayContent);
@@ -1269,7 +1269,7 @@
                     // We also don't clear the mAnimatingExit flag for windows which have the
                     // mRemoveOnExit flag. This indicates an explicit remove request has been issued
                     // by the client. We should let animation proceed and not clear this flag or
-                    // they won't eventually be removed by WindowStateAnimator#finishExit.
+                    // they won't eventually be removed by WindowState#onExitAnimationDone.
                     if (!win.mWillReplaceWindow && !win.mRemoveOnExit) {
                         win.mAnimatingExit = false;
                         // Clear mAnimating flag together with mAnimatingExit. When animation