Switched DisplayContent to use WindowContainer

Bug: 30060889
Test: Manual testing and existing tests still pass.
Change-Id: I99f2e38da417f62e8aa65bb6582aba53fd528c1b
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 4fc3ab0..1c83a9b 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -20,9 +20,11 @@
 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.HOME_STACK_ID;
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.view.WindowManager.DOCKED_BOTTOM;
+import static android.view.WindowManager.DOCKED_INVALID;
+import static android.view.WindowManager.DOCKED_TOP;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
@@ -32,6 +34,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
@@ -53,7 +56,6 @@
 import android.view.DisplayInfo;
 import android.view.IWindow;
 import android.view.Surface;
-import android.view.animation.Animation;
 import com.android.internal.util.FastPrintWriter;
 
 import java.io.FileDescriptor;
@@ -73,7 +75,7 @@
  * IMPORTANT: No method from this class should ever be used without holding
  * WindowManagerService.mWindowMap.
  */
-class DisplayContent {
+class DisplayContent extends WindowContainer<TaskStack> {
 
     /** Unique identifier of this stack. */
     private final int mDisplayId;
@@ -104,10 +106,6 @@
     /** Window tokens that are in the process of exiting, but still on screen for animations. */
     final ArrayList<WindowToken> mExitingTokens = new ArrayList<>();
 
-    /** Array containing all TaskStacks on this display.  Array
-     * is stored in display order with the current bottom stack at 0. */
-    private final ArrayList<TaskStack> mStacks = new ArrayList<>();
-
     /** A special TaskStack with id==HOME_STACK_ID that moves to the bottom whenever any TaskStack
      * (except a future lockscreen TaskStack) moves to the top. */
     private TaskStack mHomeStack = null;
@@ -193,10 +191,6 @@
         return (mDisplay.getFlags() & Display.FLAG_PRIVATE) != 0;
     }
 
-    ArrayList<TaskStack> getStacks() {
-        return mStacks;
-    }
-
     TaskStack getHomeStack() {
         if (mHomeStack == null && mDisplayId == Display.DEFAULT_DISPLAY) {
             Slog.e(TAG_WM, "getHomeStack: Returning null from this=" + this);
@@ -205,8 +199,8 @@
     }
 
     TaskStack getStackById(int stackId) {
-        for (int i = mStacks.size() - 1; i >= 0; --i) {
-            final TaskStack stack = mStacks.get(i);
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final TaskStack stack = mChildren.get(i);
             if (stack.mStackId == stackId) {
                 return stack;
             }
@@ -223,8 +217,8 @@
 
         getDockedDividerController().onConfigurationChanged();
 
-        for (int i = 0; i < mStacks.size(); i++) {
-            final TaskStack stack = mStacks.get(i);
+        for (int i = 0; i < mChildren.size(); i++) {
+            final TaskStack stack = mChildren.get(i);
             if (stack.onConfigurationChanged()) {
                 changedStackList.add(stack.mStackId);
             }
@@ -232,39 +226,23 @@
     }
 
     void checkAppWindowsReadyToShow() {
-        for (int i = mStacks.size() - 1; i >= 0; --i) {
-            final TaskStack stack = mStacks.get(i);
-            stack.checkAppWindowsReadyToShow(mDisplayId);
-        }
+        super.checkAppWindowsReadyToShow(mDisplayId);
     }
 
     void updateAllDrawn() {
-        for (int i = mStacks.size() - 1; i >= 0; --i) {
-            final TaskStack stack = mStacks.get(i);
-            stack.updateAllDrawn(mDisplayId);
-        }
+        super.updateAllDrawn(mDisplayId);
     }
 
     void stepAppWindowsAnimation(long currentTime) {
-        for (int i = mStacks.size() - 1; i >= 0; --i) {
-            final TaskStack stack = mStacks.get(i);
-            stack.stepAppWindowsAnimation(currentTime, mDisplayId);
-        }
+        super.stepAppWindowsAnimation(currentTime, mDisplayId);
     }
 
     void onAppTransitionDone() {
-        for (int i = mStacks.size() - 1; i >= 0; --i) {
-            final TaskStack stack = mStacks.get(i);
-            stack.onAppTransitionDone();
-        }
-
+        super.onAppTransitionDone();
         rebuildAppWindowList();
     }
 
     int getOrientation() {
-        // TODO: Most of the logic here can be removed once this class is converted to use
-        // WindowContainer which has an abstract implementation of getOrientation that
-        // should cover this.
         if (mService.isStackVisibleLocked(DOCKED_STACK_ID)
                 || mService.isStackVisibleLocked(FREEFORM_WORKSPACE_STACK_ID)) {
             // Apps and their containers are not allowed to specify an orientation while the docked
@@ -280,23 +258,11 @@
             return SCREEN_ORIENTATION_UNSPECIFIED;
         }
 
-        for (int i = mStacks.size() - 1; i >= 0; --i) {
-            final TaskStack stack = mStacks.get(i);
-            if (!stack.isVisible()) {
-                continue;
-            }
-
-            final int orientation = stack.getOrientation();
-
-            if (orientation == SCREEN_ORIENTATION_BEHIND) {
-                continue;
-            }
-
-            if (orientation != SCREEN_ORIENTATION_UNSET) {
-                if (stack.fillsParent() || orientation != SCREEN_ORIENTATION_UNSPECIFIED) {
-                    return orientation;
-                }
-            }
+        final int orientation = super.getOrientation();
+        if (orientation != SCREEN_ORIENTATION_UNSET) {
+            if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
+                    "App is requesting an orientation, return " + orientation);
+            return orientation;
         }
 
         if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
@@ -309,8 +275,8 @@
     void updateDisplayInfo() {
         mDisplay.getDisplayInfo(mDisplayInfo);
         mDisplay.getMetrics(mDisplayMetrics);
-        for (int i = mStacks.size() - 1; i >= 0; --i) {
-            mStacks.get(i).updateDisplayInfo(null);
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            mChildren.get(i).updateDisplayInfo(null);
         }
     }
 
@@ -356,11 +322,7 @@
             }
             mHomeStack = stack;
         }
-        if (onTop) {
-            mStacks.add(stack);
-        } else {
-            mStacks.add(0, stack);
-        }
+        addChild(stack, onTop ? mChildren.size() : 0);
         layoutNeeded = true;
     }
 
@@ -371,11 +333,12 @@
             return;
         }
 
-        if (!mStacks.remove(stack)) {
+        if (!mChildren.contains(stack)) {
             Slog.wtf(TAG_WM, "moving stack that was not added: " + stack, new Throwable());
         }
+        removeChild(stack);
 
-        int addIndex = toTop ? mStacks.size() : 0;
+        int addIndex = toTop ? mChildren.size() : 0;
 
         if (toTop
                 && mService.isStackVisibleLocked(PINNED_STACK_ID)
@@ -383,30 +346,12 @@
             // The pinned stack is always the top most stack (always-on-top) when it is visible.
             // So, stack is moved just below the pinned stack.
             addIndex--;
-            TaskStack topStack = mStacks.get(addIndex);
+            TaskStack topStack = mChildren.get(addIndex);
             if (topStack.mStackId != PINNED_STACK_ID) {
-                throw new IllegalStateException("Pinned stack isn't top stack??? " + mStacks);
+                throw new IllegalStateException("Pinned stack isn't top stack??? " + mChildren);
             }
         }
-        mStacks.add(addIndex, stack);
-    }
-
-    // TODO: Don't forget to switch to WC.removeChild
-    void detachChild(TaskStack stack) {
-        detachStack(stack);
-        if (stack.detachFromDisplay()) {
-            mService.mWindowPlacerLocked.requestTraversal();
-        }
-        if (stack.mStackId == DOCKED_STACK_ID) {
-            mService.getDefaultDisplayContentLocked().mDividerControllerLocked
-                    .notifyDockedStackExistsChanged(false);
-        }
-    }
-
-    // TODO: See about removing this by untangling the use case in WMS.attachStack()
-    void detachStack(TaskStack stack) {
-        mDimLayerController.removeDimLayerUser(stack);
-        mStacks.remove(stack);
+        addChild(stack, addIndex);
     }
 
     /**
@@ -418,8 +363,8 @@
     }
 
     int taskIdFromPoint(int x, int y) {
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            final TaskStack stack = mStacks.get(stackNdx);
+        for (int stackNdx = mChildren.size() - 1; stackNdx >= 0; --stackNdx) {
+            final TaskStack stack = mChildren.get(stackNdx);
             final int taskId = stack.taskIdFromPoint(x, y);
             if (taskId != -1) {
                 return taskId;
@@ -435,8 +380,8 @@
     Task findTaskForResizePoint(int x, int y) {
         final int delta = mService.dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics);
         mTmpTaskForResizePointSearchResult.reset();
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            TaskStack stack = mStacks.get(stackNdx);
+        for (int stackNdx = mChildren.size() - 1; stackNdx >= 0; --stackNdx) {
+            TaskStack stack = mChildren.get(stackNdx);
             if (!StackId.isTaskResizeAllowed(stack.mStackId)) {
                 return null;
             }
@@ -453,8 +398,8 @@
         mTouchExcludeRegion.set(mBaseDisplayRect);
         final int delta = mService.dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics);
         mTmpRect2.setEmpty();
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            final TaskStack stack = mStacks.get(stackNdx);
+        for (int stackNdx = mChildren.size() - 1; stackNdx >= 0; --stackNdx) {
+            final TaskStack stack = mChildren.get(stackNdx);
             stack.setTouchExcludeRegion(
                     focusedTask, delta, mTouchExcludeRegion, mContentRect, mTmpRect2);
         }
@@ -498,16 +443,16 @@
             }
         }
 
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            mStacks.get(stackNdx).switchUser();
+        for (int stackNdx = mChildren.size() - 1; stackNdx >= 0; --stackNdx) {
+            mChildren.get(stackNdx).switchUser();
         }
 
         rebuildAppWindowList();
     }
 
     void resetAnimationBackgroundAnimator() {
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            mStacks.get(stackNdx).resetAnimationBackgroundAnimator();
+        for (int stackNdx = mChildren.size() - 1; stackNdx >= 0; --stackNdx) {
+            mChildren.get(stackNdx).resetAnimationBackgroundAnimator();
         }
     }
 
@@ -527,37 +472,142 @@
         mDimLayerController.stopDimmingIfNeeded();
     }
 
-    void close() {
-        mDimLayerController.close();
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            mStacks.get(stackNdx).close();
+    @Override
+    void removeIfPossible() {
+        if (isAnimating()) {
+            mDeferredRemoval = true;
+            return;
         }
+        removeImmediately();
     }
 
-    boolean isAnimating() {
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            final TaskStack stack = mStacks.get(stackNdx);
-            if (stack.isAnimating()) {
-                return true;
-            }
+    @Override
+    void removeImmediately() {
+        super.removeImmediately();
+        if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Removing display=" + this);
+        mService.mDisplayContents.delete(mDisplayId);
+        mDimLayerController.close();
+        if (mDisplayId == Display.DEFAULT_DISPLAY) {
+            mService.unregisterPointerEventListener(mTapDetector);
+            mService.unregisterPointerEventListener(mService.mMousePositionTracker);
         }
-        return false;
     }
 
     /** Returns true if a removal action is still being deferred. */
+    @Override
     boolean checkCompleteDeferredRemoval() {
-        boolean stillDeferringRemoval = false;
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            final TaskStack stack = mStacks.get(stackNdx);
-            stillDeferringRemoval |= stack.checkCompleteDeferredRemoval();
-        }
+        final boolean stillDeferringRemoval = super.checkCompleteDeferredRemoval();
+
         if (!stillDeferringRemoval && mDeferredRemoval) {
+            removeImmediately();
             mService.onDisplayRemoved(mDisplayId);
             return false;
         }
         return true;
     }
 
+    boolean animateForIme(float interpolatedValue, float animationTarget,
+            float dividerAnimationTarget) {
+        boolean updated = false;
+
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final TaskStack stack = mChildren.get(i);
+            if (stack == null || !stack.isAdjustedForIme()) {
+                continue;
+            }
+
+            if (interpolatedValue >= 1f && animationTarget == 0f && dividerAnimationTarget == 0f) {
+                stack.resetAdjustedForIme(true /* adjustBoundsNow */);
+                updated = true;
+            } else {
+                mDividerControllerLocked.mLastAnimationProgress =
+                        mDividerControllerLocked.getInterpolatedAnimationValue(interpolatedValue);
+                mDividerControllerLocked.mLastDividerProgress =
+                        mDividerControllerLocked.getInterpolatedDividerValue(interpolatedValue);
+                updated |= stack.updateAdjustForIme(
+                        mDividerControllerLocked.mLastAnimationProgress,
+                        mDividerControllerLocked.mLastDividerProgress,
+                        false /* force */);
+            }
+            if (interpolatedValue >= 1f) {
+                stack.endImeAdjustAnimation();
+            }
+        }
+
+        return updated;
+    }
+
+    boolean clearImeAdjustAnimation() {
+        boolean changed = false;
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final TaskStack stack = mChildren.get(i);
+            if (stack != null && stack.isAdjustedForIme()) {
+                stack.resetAdjustedForIme(true /* adjustBoundsNow */);
+                changed  = true;
+            }
+        }
+        return changed;
+    }
+
+    void beginImeAdjustAnimation() {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final TaskStack stack = mChildren.get(i);
+            if (stack.isVisible() && stack.isAdjustedForIme()) {
+                stack.beginImeAdjustAnimation();
+            }
+        }
+    }
+
+    void adjustForImeIfNeeded() {
+        final WindowState imeWin = mService.mInputMethodWindow;
+        final boolean imeVisible = imeWin != null && imeWin.isVisibleLw() && imeWin.isDisplayedLw()
+                && !mDividerControllerLocked.isImeHideRequested();
+        final boolean dockVisible = mService.isStackVisibleLocked(DOCKED_STACK_ID);
+        final TaskStack imeTargetStack = mService.getImeFocusStackLocked();
+        final int imeDockSide = (dockVisible && imeTargetStack != null) ?
+                imeTargetStack.getDockSide() : DOCKED_INVALID;
+        final boolean imeOnTop = (imeDockSide == DOCKED_TOP);
+        final boolean imeOnBottom = (imeDockSide == DOCKED_BOTTOM);
+        final boolean dockMinimized = mDividerControllerLocked.isMinimizedDock();
+        final int imeHeight = mService.mPolicy.getInputMethodWindowVisibleHeightLw();
+        final boolean imeHeightChanged = imeVisible &&
+                imeHeight != mDividerControllerLocked.getImeHeightAdjustedFor();
+
+        // The divider could be adjusted for IME position, or be thinner than usual,
+        // or both. There are three possible cases:
+        // - If IME is visible, and focus is on top, divider is not moved for IME but thinner.
+        // - If IME is visible, and focus is on bottom, divider is moved for IME and thinner.
+        // - If IME is not visible, divider is not moved and is normal width.
+
+        if (imeVisible && dockVisible && (imeOnTop || imeOnBottom) && !dockMinimized) {
+            for (int i = mChildren.size() - 1; i >= 0; --i) {
+                final TaskStack stack = mChildren.get(i);
+                final boolean isDockedOnBottom = stack.getDockSide() == DOCKED_BOTTOM;
+                if (stack.isVisible() && (imeOnBottom || isDockedOnBottom)) {
+                    stack.setAdjustedForIme(imeWin, imeOnBottom && imeHeightChanged);
+                } else {
+                    stack.resetAdjustedForIme(false);
+                }
+            }
+            mDividerControllerLocked.setAdjustedForIme(
+                    imeOnBottom /*ime*/, true /*divider*/, true /*animate*/, imeWin, imeHeight);
+        } else {
+            for (int i = mChildren.size() - 1; i >= 0; --i) {
+                final TaskStack stack = mChildren.get(i);
+                stack.resetAdjustedForIme(!dockVisible);
+            }
+            mDividerControllerLocked.setAdjustedForIme(
+                    false /*ime*/, false /*divider*/, dockVisible /*animate*/, imeWin, imeHeight);
+        }
+    }
+
+    void prepareFreezingTaskBounds() {
+        for (int stackNdx = mChildren.size() - 1; stackNdx >= 0; --stackNdx) {
+            final TaskStack stack = mChildren.get(stackNdx);
+            stack.prepareFreezingTaskBounds();
+        }
+    }
+
     void rotateBounds(int oldRotation, int newRotation, Rect bounds) {
         final int rotationDelta = DisplayContent.deltaRotation(oldRotation, newRotation);
         getLogicalDisplayRect(mTmpRect);
@@ -624,8 +674,8 @@
 
         pw.println();
         pw.println("  Application tokens in top down Z order:");
-        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            final TaskStack stack = mStacks.get(stackNdx);
+        for (int stackNdx = mChildren.size() - 1; stackNdx >= 0; --stackNdx) {
+            final TaskStack stack = mChildren.get(stackNdx);
             stack.dump(prefix + "  ", pw);
         }
 
@@ -649,7 +699,7 @@
 
     @Override
     public String toString() {
-        return getName() + " stacks=" + mStacks;
+        return getName() + " stacks=" + mChildren;
     }
 
     String getName() {
@@ -706,15 +756,6 @@
         return touchedWin;
     }
 
-    /**
-     * See {@link WindowManagerService#overridePlayingAppAnimationsLw}.
-     */
-    void overridePlayingAppAnimationsLw(Animation a) {
-        for (int i = mStacks.size() - 1; i >= 0; i--) {
-            mStacks.get(i).overridePlayingAppAnimations(a);
-        }
-    }
-
     boolean canAddToastWindowForUid(int uid) {
         // We allow one toast window per UID being shown at a time.
         WindowList windows = getWindowList();
@@ -788,12 +829,7 @@
             // Descend through all of the app tokens and find the first that either matches
             // win.mAppToken (return win) or mFocusedApp (return null).
             if (wtoken != null && win.mAttrs.type != TYPE_APPLICATION_STARTING) {
-                final TaskStack focusedAppStack = focusedApp.mTask.mStack;
-                final TaskStack appStack = wtoken.mTask.mStack;
-
-                // TODO: Use WindowContainer.compareTo() once everything is using WindowContainer
-                if ((focusedAppStack == appStack && focusedApp.compareTo(wtoken) > 0)
-                        || mStacks.indexOf(focusedAppStack) > mStacks.indexOf(appStack)) {
+                if (focusedApp.compareTo(wtoken) > 0) {
                     // App stack below focused app stack. No focus for you!!!
                     if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM,
                             "findFocusedWindow: Reached focused app=" + focusedApp);
@@ -826,8 +862,8 @@
 
         // Figure out where the window should go, based on the order of applications.
         mTmpGetWindowOnDisplaySearchResult.reset();
-        for (int i = mStacks.size() - 1; i >= 0; --i) {
-            final TaskStack stack = mStacks.get(i);
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final TaskStack stack = mChildren.get(i);
             stack.getWindowOnDisplayBeforeToken(this, wToken, mTmpGetWindowOnDisplaySearchResult);
             if (mTmpGetWindowOnDisplaySearchResult.reachedToken) {
                 // We have reach the token we are interested in. End search.
@@ -858,8 +894,8 @@
 
         // Continue looking down until we find the first token that has windows on this display.
         mTmpGetWindowOnDisplaySearchResult.reset();
-        for (int i = mStacks.size() - 1; i >= 0; --i) {
-            final TaskStack stack = mStacks.get(i);
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final TaskStack stack = mChildren.get(i);
             stack.getWindowOnDisplayAfterToken(this, wToken, mTmpGetWindowOnDisplaySearchResult);
             if (mTmpGetWindowOnDisplaySearchResult.foundWindow != null) {
                 // We have found a window after the token. End search.
@@ -1023,9 +1059,9 @@
         // First add all of the exiting app tokens...  these are no longer in the main app list,
         // but still have windows shown. We put them in the back because now that the animation is
         // over we no longer will care about them.
-        final int numStacks = mStacks.size();
+        final int numStacks = mChildren.size();
         for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
-            AppTokenList exitingAppTokens = mStacks.get(stackNdx).mExitingAppTokens;
+            AppTokenList exitingAppTokens = mChildren.get(stackNdx).mExitingAppTokens;
             int NT = exitingAppTokens.size();
             for (int j = 0; j < NT; j++) {
                 i = exitingAppTokens.get(j).rebuildWindowList(this, i);
@@ -1034,7 +1070,7 @@
 
         // And add in the still active app tokens in Z order.
         for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
-            i = mStacks.get(stackNdx).rebuildWindowList(this, i);
+            i = mChildren.get(stackNdx).rebuildWindowList(this, i);
         }
 
         i -= lastBelow;
@@ -1158,24 +1194,11 @@
         dumpChildrenNames(pw, "  ");
     }
 
-    private void dumpChildrenNames(PrintWriter pw, String prefix) {
-        final String childPrefix = prefix + prefix;
-        for (int j = mStacks.size() - 1; j >= 0; j--) {
-            final TaskStack stack = mStacks.get(j);
-            pw.println("#" + j + " " + getName());
-            stack.dumpChildrenNames(pw, childPrefix);
-        }
-    }
-
     private void dumpWindows() {
-        final int numDisplays = mService.mDisplayContents.size();
-        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
-            final DisplayContent displayContent = mService.mDisplayContents.valueAt(displayNdx);
-            Slog.v(TAG_WM, " Display #" + displayContent.getDisplayId());
-            final WindowList windows = displayContent.getWindowList();
-            for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
-                Slog.v(TAG_WM, "  #" + winNdx + ": " + windows.get(winNdx));
-            }
+        Slog.v(TAG_WM, " Display #" + mDisplayId);
+        final WindowList windows = getWindowList();
+        for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
+            Slog.v(TAG_WM, "  #" + winNdx + ": " + windows.get(winNdx));
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index ff676e9..ef8f492 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -131,8 +131,8 @@
     private boolean mAdjustedForDivider;
     private float mDividerAnimationStart;
     private float mDividerAnimationTarget;
-    private float mLastAnimationProgress;
-    private float mLastDividerProgress;
+    float mLastAnimationProgress;
+    float mLastDividerProgress;
     private final DividerSnapAlgorithm[] mSnapAlgorithmForRotation = new DividerSnapAlgorithm[4];
     private boolean mImeHideRequested;
 
@@ -594,15 +594,7 @@
     }
 
     private boolean clearImeAdjustAnimation() {
-        boolean changed = false;
-        final ArrayList<TaskStack> stacks = mDisplayContent.getStacks();
-        for (int i = stacks.size() - 1; i >= 0; --i) {
-            final TaskStack stack = stacks.get(i);
-            if (stack != null && stack.isAdjustedForIme()) {
-                stack.resetAdjustedForIme(true /* adjustBoundsNow */);
-                changed  = true;
-            }
-        }
+        final boolean changed = mDisplayContent.clearImeAdjustAnimation();
         mAnimatingForIme = false;
         return changed;
     }
@@ -634,13 +626,7 @@
         mAnimationTarget = adjustedForIme ? 1 : 0;
         mDividerAnimationTarget = adjustedForDivider ? 1 : 0;
 
-        final ArrayList<TaskStack> stacks = mDisplayContent.getStacks();
-        for (int i = stacks.size() - 1; i >= 0; --i) {
-            final TaskStack stack = stacks.get(i);
-            if (stack.isVisible() && stack.isAdjustedForIme()) {
-                stack.beginImeAdjustAnimation();
-            }
-        }
+        mDisplayContent.beginImeAdjustAnimation();
 
         // We put all tasks into drag resizing mode - wait until all of them have completed the
         // drag resizing switch.
@@ -721,27 +707,8 @@
         float t = Math.min(1f, (float) (now - mAnimationStartTime) / mAnimationDuration);
         t = (mAnimationTarget == 1f ? IME_ADJUST_ENTRY_INTERPOLATOR : TOUCH_RESPONSE_INTERPOLATOR)
                 .getInterpolation(t);
-        final ArrayList<TaskStack> stacks = mDisplayContent.getStacks();
-        boolean updated = false;
-        for (int i = stacks.size() - 1; i >= 0; --i) {
-            final TaskStack stack = stacks.get(i);
-            if (stack != null && stack.isAdjustedForIme()) {
-                if (t >= 1f && mAnimationTarget == 0f && mDividerAnimationTarget == 0f) {
-                    stack.resetAdjustedForIme(true /* adjustBoundsNow */);
-                    updated = true;
-                } else {
-                    mLastAnimationProgress = getInterpolatedAnimationValue(t);
-                    mLastDividerProgress = getInterpolatedDividerValue(t);
-                    updated |= stack.updateAdjustForIme(
-                            mLastAnimationProgress,
-                            mLastDividerProgress,
-                            false /* force */);
-                }
-                if (t >= 1f) {
-                    stack.endImeAdjustAnimation();
-                }
-            }
-        }
+        final boolean updated =
+                mDisplayContent.animateForIme(t, mAnimationTarget, mDividerAnimationTarget);
         if (updated) {
             mService.mWindowPlacerLocked.performSurfacePlacement();
         }
@@ -785,11 +752,11 @@
         }
     }
 
-    private float getInterpolatedAnimationValue(float t) {
+    float getInterpolatedAnimationValue(float t) {
         return t * mAnimationTarget + (1 - t) * mAnimationStart;
     }
 
-    private float getInterpolatedDividerValue(float t) {
+    float getInterpolatedDividerValue(float t) {
         return t * mDividerAnimationTarget + (1 - t) * mDividerAnimationStart;
     }
 
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index b24a06a..d6a1fae 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -110,7 +110,7 @@
 
     /** Detach this stack from its display when animation completes. */
     // TODO: maybe tie this to WindowContainer#removeChild some how...
-    boolean mDeferDetach;
+    boolean mDeferRemoval;
 
     private final Rect mTmpAdjustedBounds = new Rect();
     private boolean mAdjustedForIme;
@@ -764,12 +764,34 @@
                 1 /*allowResizeInDockedMode*/, bounds).sendToTarget();
     }
 
-    // TODO: Should this really be removeImmidiately or removeChild?
-    boolean detachFromDisplay() {
+    @Override
+    void removeIfPossible() {
+        if (isAnimating()) {
+            mDeferRemoval = true;
+            return;
+        }
+        removeImmediately();
+    }
+
+    @Override
+    void removeImmediately() {
+        super.removeImmediately();
+
+        mDisplayContent.mDimLayerController.removeDimLayerUser(this);
         EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId);
-        boolean didSomething = super.detachFromDisplay();
-        close();
-        return didSomething;
+
+        if (mAnimationBackgroundSurface != null) {
+            mAnimationBackgroundSurface.destroySurface();
+            mAnimationBackgroundSurface = null;
+        }
+        mDisplayContent = null;
+
+        mService.mWindowPlacerLocked.requestTraversal();
+
+        if (mStackId == DOCKED_STACK_ID) {
+            mService.getDefaultDisplayContentLocked().mDividerControllerLocked
+                    .notifyDockedStackExistsChanged(false);
+        }
     }
 
     void resetAnimationBackgroundAnimator() {
@@ -801,14 +823,6 @@
         }
     }
 
-    void close() {
-        if (mAnimationBackgroundSurface != null) {
-            mAnimationBackgroundSurface.destroySurface();
-            mAnimationBackgroundSurface = null;
-        }
-        mDisplayContent = null;
-    }
-
     /**
      * Adjusts the stack bounds if the IME is visible.
      *
@@ -1077,7 +1091,7 @@
 
     public void dump(String prefix, PrintWriter pw) {
         pw.println(prefix + "mStackId=" + mStackId);
-        pw.println(prefix + "mDeferDetach=" + mDeferDetach);
+        pw.println(prefix + "mDeferRemoval=" + mDeferRemoval);
         pw.println(prefix + "mFillsParent=" + mFillsParent);
         pw.println(prefix + "mBounds=" + mBounds.toShortString());
         if (mMinimizeAmount != 0f) {
@@ -1411,8 +1425,8 @@
         if (isAnimating()) {
             return true;
         }
-        if (mDeferDetach) {
-            mDisplayContent.detachChild(this);
+        if (mDeferRemoval) {
+            removeImmediately();
         }
 
         return super.checkCompleteDeferredRemoval();
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 106284a..4862265 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -187,15 +187,6 @@
         }
     }
 
-    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);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a3c830a..e4f6c27 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -173,9 +173,7 @@
 import static android.app.StatusBarManager.DISABLE_MASK;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static android.view.WindowManager.DOCKED_BOTTOM;
 import static android.view.WindowManager.DOCKED_INVALID;
-import static android.view.WindowManager.DOCKED_TOP;
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
@@ -3153,13 +3151,8 @@
 
     private void prepareFreezingAllTaskBounds() {
         for (int i = mDisplayContents.size() - 1; i >= 0; i--) {
-            ArrayList<TaskStack> stacks = mDisplayContents.valueAt(i).getStacks();
-            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
-                final TaskStack stack = stacks.get(stackNdx);
-                stack.prepareFreezingTaskBounds();
-            }
+            mDisplayContents.valueAt(i).prepareFreezingTaskBounds();
         }
-
     }
 
     private int[] onConfigurationChanged() {
@@ -3986,10 +3979,8 @@
 
                         stack = displayContent.getStackById(stackId);
                         if (stack != null) {
-                            // It's already attached to the display. Detach and re-attach
-                            // because onTop might change, and be sure to clear mDeferDetach!
-                            displayContent.detachStack(stack);
-                            stack.mDeferDetach = false;
+                            // It's already attached to the display...clear mDeferRemoval!
+                            stack.mDeferRemoval = false;
                             attachedToDisplay = true;
                         } else {
                             stack = new TaskStack(this, stackId);
@@ -4023,14 +4014,7 @@
         synchronized (mWindowMap) {
             final TaskStack stack = mStackIdToStack.get(stackId);
             if (stack != null) {
-                final DisplayContent displayContent = stack.getDisplayContent();
-                if (displayContent != null) {
-                    if (stack.isAnimating()) {
-                        stack.mDeferDetach = true;
-                        return;
-                    }
-                    displayContent.detachChild(stack);
-                }
+                stack.removeIfPossible();
             }
         }
     }
@@ -4135,7 +4119,7 @@
 
     @Override
     public void overridePlayingAppAnimationsLw(Animation a) {
-        getDefaultDisplayContentLocked().overridePlayingAppAnimationsLw(a);
+        getDefaultDisplayContentLocked().overridePlayingAppAnimations(a);
     }
 
     /**
@@ -4751,7 +4735,7 @@
             hideBootMessagesLocked();
             // If the screen still doesn't come up after 30 seconds, give
             // up and turn it on.
-            mH.sendEmptyMessageDelayed(H.BOOT_TIMEOUT, 30*1000);
+            mH.sendEmptyMessageDelayed(H.BOOT_TIMEOUT, 30 * 1000);
         }
 
         mPolicy.systemBooted();
@@ -6689,51 +6673,6 @@
         }
     }
 
-    void adjustForImeIfNeeded(final DisplayContent displayContent) {
-        final WindowState imeWin = mInputMethodWindow;
-        final boolean imeVisible = imeWin != null && imeWin.isVisibleLw() && imeWin.isDisplayedLw()
-                && !displayContent.mDividerControllerLocked.isImeHideRequested();
-        final boolean dockVisible = isStackVisibleLocked(DOCKED_STACK_ID);
-        final TaskStack imeTargetStack = getImeFocusStackLocked();
-        final int imeDockSide = (dockVisible && imeTargetStack != null) ?
-                imeTargetStack.getDockSide() : DOCKED_INVALID;
-        final boolean imeOnTop = (imeDockSide == DOCKED_TOP);
-        final boolean imeOnBottom = (imeDockSide == DOCKED_BOTTOM);
-        final boolean dockMinimized = displayContent.mDividerControllerLocked.isMinimizedDock();
-        final int imeHeight = mPolicy.getInputMethodWindowVisibleHeightLw();
-        final boolean imeHeightChanged = imeVisible &&
-                imeHeight != displayContent.mDividerControllerLocked.getImeHeightAdjustedFor();
-
-        // The divider could be adjusted for IME position, or be thinner than usual,
-        // or both. There are three possible cases:
-        // - If IME is visible, and focus is on top, divider is not moved for IME but thinner.
-        // - If IME is visible, and focus is on bottom, divider is moved for IME and thinner.
-        // - If IME is not visible, divider is not moved and is normal width.
-
-        if (imeVisible && dockVisible && (imeOnTop || imeOnBottom) && !dockMinimized) {
-            final ArrayList<TaskStack> stacks = displayContent.getStacks();
-            for (int i = stacks.size() - 1; i >= 0; --i) {
-                final TaskStack stack = stacks.get(i);
-                final boolean isDockedOnBottom = stack.getDockSide() == DOCKED_BOTTOM;
-                if (stack.isVisible() && (imeOnBottom || isDockedOnBottom)) {
-                    stack.setAdjustedForIme(imeWin, imeOnBottom && imeHeightChanged);
-                } else {
-                    stack.resetAdjustedForIme(false);
-                }
-            }
-            displayContent.mDividerControllerLocked.setAdjustedForIme(
-                    imeOnBottom /*ime*/, true /*divider*/, true /*animate*/, imeWin, imeHeight);
-        } else {
-            final ArrayList<TaskStack> stacks = displayContent.getStacks();
-            for (int i = stacks.size() - 1; i >= 0; --i) {
-                final TaskStack stack = stacks.get(i);
-                stack.resetAdjustedForIme(!dockVisible);
-            }
-            displayContent.mDividerControllerLocked.setAdjustedForIme(
-                    false /*ime*/, false /*divider*/, dockVisible /*animate*/, imeWin, imeHeight);
-        }
-    }
-
     // -------------------------------------------------------------
     // Drag and drop
     // -------------------------------------------------------------
@@ -7636,7 +7575,7 @@
                     synchronized (mWindowMap) {
                         final DisplayContent displayContent = getDefaultDisplayContentLocked();
                         displayContent.getDockedDividerController().reevaluateVisibility(false);
-                        adjustForImeIfNeeded(displayContent);
+                        displayContent.adjustForImeIfNeeded();
                     }
                 }
                 break;
@@ -8619,7 +8558,7 @@
                 mInputMonitor.setInputFocusLw(mCurrentFocus, updateInputWindows);
             }
 
-            adjustForImeIfNeeded(displayContent);
+            displayContent.adjustForImeIfNeeded();
 
             // We may need to schedule some toast windows to be removed. The
             // toasts for an app that does not have input focus are removed
@@ -9753,17 +9692,7 @@
     private void handleDisplayRemovedLocked(int displayId) {
         final DisplayContent displayContent = getDisplayContentLocked(displayId);
         if (displayContent != null) {
-            if (displayContent.isAnimating()) {
-                displayContent.mDeferredRemoval = true;
-                return;
-            }
-            if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Removing display=" + displayContent);
-            mDisplayContents.delete(displayId);
-            displayContent.close();
-            if (displayId == Display.DEFAULT_DISPLAY) {
-                unregisterPointerEventListener(displayContent.mTapDetector);
-                unregisterPointerEventListener(mMousePositionTracker);
-            }
+            displayContent.removeIfPossible();
         }
         mAnimator.removeDisplayLocked(displayId);
         mWindowPlacerLocked.requestTraversal();
@@ -10030,7 +9959,7 @@
         }
     }
 
-    private MousePositionTracker mMousePositionTracker = new MousePositionTracker();
+    MousePositionTracker mMousePositionTracker = new MousePositionTracker();
 
     private static class MousePositionTracker implements PointerEventListener {
         private boolean mLatestEventWasMouse;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 60b0d03..1c0d830 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1816,15 +1816,6 @@
         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();
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 125368c..ce101e7 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1750,7 +1750,7 @@
         Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
 
         if (mWin.mAttrs.type == TYPE_INPUT_METHOD) {
-            mService.adjustForImeIfNeeded(mWin.mDisplayContent);
+            mWin.mDisplayContent.adjustForImeIfNeeded();
             if (isEntrance) {
                 mWin.setDisplayLayoutNeeded();
                 mService.mWindowPlacerLocked.requestTraversal();
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 973f1b9..eb2372a 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
@@ -178,25 +178,6 @@
     }
 
     @Test
-    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());
-    }
-
-    @Test
     public void testIsAnimating() throws Exception {
         final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
         final TestWindowContainer root = builder.setLayer(0).build();
@@ -399,7 +380,6 @@
     /* Used so we can gain access to some protected members of the {@link WindowContainer} class */
     private class TestWindowContainer extends WindowContainer<TestWindowContainer> {
         private final int mLayer;
-        private final boolean mCanDetach;
         private boolean mIsAnimating;
         private boolean mIsVisible;
         private boolean mFillsParent;
@@ -419,9 +399,8 @@
             return 1;
         };
 
-        TestWindowContainer(int layer, boolean canDetach, boolean isAnimating, boolean isVisible) {
+        TestWindowContainer(int layer, boolean isAnimating, boolean isVisible) {
             mLayer = layer;
-            mCanDetach = canDetach;
             mIsAnimating = isAnimating;
             mIsVisible = isVisible;
             mFillsParent = true;
@@ -455,11 +434,6 @@
         }
 
         @Override
-        boolean detachFromDisplay() {
-            return super.detachFromDisplay() || mCanDetach;
-        }
-
-        @Override
         boolean isAnimating() {
             return mIsAnimating || super.isAnimating();
         }
@@ -481,7 +455,6 @@
 
     private class TestWindowContainerBuilder {
         private int mLayer;
-        private boolean mCanDetach;
         private boolean mIsAnimating;
         private boolean mIsVisible;
 
@@ -494,11 +467,6 @@
             return this;
         }
 
-        TestWindowContainerBuilder setCanDetach(boolean canDetach) {
-            mCanDetach = canDetach;
-            return this;
-        }
-
         TestWindowContainerBuilder setIsAnimating(boolean isAnimating) {
             mIsAnimating = isAnimating;
             return this;
@@ -511,14 +479,13 @@
 
         TestWindowContainerBuilder reset() {
             mLayer = 0;
-            mCanDetach = false;
             mIsAnimating = false;
             mIsVisible = false;
             return this;
         }
 
         TestWindowContainer build() {
-            return new TestWindowContainer(mLayer, mCanDetach, mIsAnimating, mIsVisible);
+            return new TestWindowContainer(mLayer, mIsAnimating, mIsVisible);
         }
     }
 }