Added StackWindowContainerController

For linking ActivityStack in AMS to TaskStack window container in WMS.

Change-Id: I8b9eaef49e62854d59b22d27f80f5935a5a4d7fc
Bug: 30060889
Test: bit FrameworksServicesTests:com.android.server.wm.StackWindowContainerControllerTests
Test: bit FrameworksServicesTests:com.android.server.wm.TaskWindowContainerControllerTests
Test: Existing test pass and manual testing.
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 914cc8d..3a74ded 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -57,6 +57,7 @@
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS;
@@ -69,6 +70,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREEN_ON;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_STACK_CRAWLS;
@@ -634,6 +636,13 @@
      */
     DisplayContent(Display display, WindowManagerService service,
             WindowLayersController layersController, WallpaperController wallpaperController) {
+
+        if (service.mRoot.getDisplayContent(display.getDisplayId()) != null) {
+            throw new IllegalArgumentException("Display with ID=" + display.getDisplayId()
+                    + " already exists=" + service.mRoot.getDisplayContent(display.getDisplayId())
+                    + " new=" + display);
+        }
+
         mDisplay = display;
         mDisplayId = display.getDisplayId();
         mLayersController = layersController;
@@ -957,75 +966,43 @@
         out.set(mContentRect);
     }
 
-    /**
-     * Adds the stack to this display.
-     * @see WindowManagerService#addStackToDisplay(int, int, boolean)
-     */
-    Rect addStackToDisplay(int stackId, boolean onTop) {
-        boolean attachedToDisplay = false;
-        TaskStack stack = mService.mStackIdToStack.get(stackId);
-        if (stack == null) {
-            if (DEBUG_STACK) Slog.d(TAG_WM, "Create new stackId=" + stackId + " on displayId="
-                    + mDisplayId);
+    TaskStack addStackToDisplay(int stackId, boolean onTop) {
+        if (DEBUG_STACK) Slog.d(TAG_WM, "Create new stackId=" + stackId + " on displayId="
+                + mDisplayId);
 
-            stack = getStackById(stackId);
-            if (stack != null) {
-                // It's already attached to the display...clear mDeferRemoval and move stack to
-                // appropriate z-order on display as needed.
-                stack.mDeferRemoval = false;
-                // We're not moving the display to front when we're adding stacks, only when
-                // requested to change the position of stack explicitly.
-                mTaskStackContainers.positionChildAt(onTop ? POSITION_TOP : POSITION_BOTTOM, stack,
-                        false /* includingParents */);
-                attachedToDisplay = true;
-            } else {
-                stack = new TaskStack(mService, stackId);
-            }
-
-            mService.mStackIdToStack.put(stackId, stack);
-            if (stackId == DOCKED_STACK_ID) {
-                mDividerControllerLocked.notifyDockedStackExistsChanged(true);
-            }
+        TaskStack stack = getStackById(stackId);
+        if (stack != null) {
+            // It's already attached to the display...clear mDeferRemoval and move stack to
+            // appropriate z-order on display as needed.
+            stack.mDeferRemoval = false;
+            // We're not moving the display to front when we're adding stacks, only when
+            // requested to change the position of stack explicitly.
+            mTaskStackContainers.positionChildAt(onTop ? POSITION_TOP : POSITION_BOTTOM, stack,
+                    false /* includingParents */);
         } else {
-            final DisplayContent currentDC = stack.getDisplayContent();
-            if (currentDC != null) {
-                throw new IllegalStateException("Trying to add stackId=" + stackId
-                        + "to displayId=" + mDisplayId + ", but it's already attached to displayId="
-                        + currentDC.getDisplayId());
-            }
-        }
-
-        if (!attachedToDisplay) {
+            stack = new TaskStack(mService, stackId);
             mTaskStackContainers.addStackToDisplay(stack, onTop);
         }
 
-        if (stack.getRawFullscreen()) {
-            return null;
+        if (stackId == DOCKED_STACK_ID) {
+            mDividerControllerLocked.notifyDockedStackExistsChanged(true);
         }
-        final Rect bounds = new Rect();
-        stack.getRawBounds(bounds);
-        return bounds;
+        return stack;
     }
 
-    /** Removes the stack from the display and prepares for changing the parent. */
-    private void removeStackFromDisplay(TaskStack stack) {
-        mTaskStackContainers.removeStackFromDisplay(stack);
-    }
-
-    /** Moves the stack to this display and returns the updated bounds. */
-    Rect moveStackToDisplay(TaskStack stack) {
-        final DisplayContent currentDisplayContent = stack.getDisplayContent();
-        if (currentDisplayContent == null) {
+    void moveStackToDisplay(TaskStack stack) {
+        final DisplayContent prevDc = stack.getDisplayContent();
+        if (prevDc == null) {
             throw new IllegalStateException("Trying to move stackId=" + stack.mStackId
                     + " which is not currently attached to any display");
         }
-        if (stack.getDisplayContent().getDisplayId() == mDisplayId) {
+        if (prevDc.getDisplayId() == mDisplayId) {
             throw new IllegalArgumentException("Trying to move stackId=" + stack.mStackId
                     + " to its current displayId=" + mDisplayId);
         }
 
-        currentDisplayContent.removeStackFromDisplay(stack);
-        return addStackToDisplay(stack.mStackId, true /* onTop */);
+        prevDc.mTaskStackContainers.removeStackFromDisplay(stack);
+        mTaskStackContainers.addStackToDisplay(stack, true /* onTop */);
     }
 
     @Override
@@ -1172,7 +1149,7 @@
             super.removeImmediately();
             if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Removing display=" + this);
             mDimLayerController.close();
-            if (mDisplayId == DEFAULT_DISPLAY) {
+            if (mDisplayId == DEFAULT_DISPLAY && mService.canDispatchPointerEvents()) {
                 mService.unregisterPointerEventListener(mTapDetector);
                 mService.unregisterPointerEventListener(mService.mMousePositionTracker);
             }
@@ -1250,7 +1227,7 @@
         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 boolean dockVisible = isStackVisible(DOCKED_STACK_ID);
         final TaskStack imeTargetStack = mService.getImeFocusStackLocked();
         final int imeDockSide = (dockVisible && imeTargetStack != null) ?
                 imeTargetStack.getDockSide() : DOCKED_INVALID;
@@ -1447,7 +1424,7 @@
      * @return The docked stack, but only if it is visible, and {@code null} otherwise.
      */
     TaskStack getDockedStackLocked() {
-        final TaskStack stack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
+        final TaskStack stack = getStackById(DOCKED_STACK_ID);
         return (stack != null && stack.isVisible()) ? stack : null;
     }
 
@@ -1456,7 +1433,7 @@
      * visible.
      */
     TaskStack getDockedStackIgnoringVisibility() {
-        return mService.mStackIdToStack.get(DOCKED_STACK_ID);
+        return getStackById(DOCKED_STACK_ID);
     }
 
     /** Find the visible, touch-deliverable window under the given point */
@@ -1532,6 +1509,9 @@
         }
     }
 
+    // TODO: This should probably be called any time a visual change is made to the hierarchy like
+    // moving containers or resizing them. Need to investigate the best way to have it automatically
+    // happen so we don't run into issues with programmers forgetting to do it.
     void layoutAndAssignWindowLayersIfNeeded() {
         mService.mWindowsChanged = true;
         setLayoutNeeded();
@@ -2441,6 +2421,27 @@
         }
     }
 
+    void setExitingTokensHasVisible(boolean hasVisible) {
+        for (int i = mExitingTokens.size() - 1; i >= 0; i--) {
+            mExitingTokens.get(i).hasVisible = hasVisible;
+        }
+
+        // Initialize state of exiting applications.
+        mTaskStackContainers.setExitingTokensHasVisible(hasVisible);
+    }
+
+    void removeExistingTokensIfPossible() {
+        for (int i = mExitingTokens.size() - 1; i >= 0; i--) {
+            final WindowToken token = mExitingTokens.get(i);
+            if (!token.hasVisible) {
+                mExitingTokens.remove(i);
+            }
+        }
+
+        // Time to remove any exiting applications?
+        mTaskStackContainers.removeExistingAppTokensIfPossible();
+    }
+
     static final class TaskForResizePointSearchResult {
         boolean searchDone;
         Task taskForResize;
@@ -2650,10 +2651,38 @@
             return false;
         }
 
+        void setExitingTokensHasVisible(boolean hasVisible) {
+            for (int i = mChildren.size() - 1; i >= 0; --i) {
+                final AppTokenList appTokens = mChildren.get(i).mExitingAppTokens;
+                for (int j = appTokens.size() - 1; j >= 0; --j) {
+                    appTokens.get(j).hasVisible = hasVisible;
+                }
+            }
+        }
+
+        void removeExistingAppTokensIfPossible() {
+            for (int i = mChildren.size() - 1; i >= 0; --i) {
+                final AppTokenList appTokens = mChildren.get(i).mExitingAppTokens;
+                for (int j = appTokens.size() - 1; j >= 0; --j) {
+                    final AppWindowToken token = appTokens.get(j);
+                    if (!token.hasVisible && !mService.mClosingApps.contains(token)
+                            && (!token.mIsExiting || token.isEmpty())) {
+                        // Make sure there is no animation running on this token, so any windows
+                        // associated with it will be removed as soon as their animations are
+                        // complete.
+                        token.mAppAnimator.clearAnimation();
+                        token.mAppAnimator.animating = false;
+                        if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG,
+                                "performLayout: App token exiting now removed" + token);
+                        token.removeIfPossible();
+                    }
+                }
+            }
+        }
+
         @Override
         int getOrientation() {
-            if (mService.isStackVisibleLocked(DOCKED_STACK_ID)
-                    || mService.isStackVisibleLocked(FREEFORM_WORKSPACE_STACK_ID)) {
+            if (isStackVisible(DOCKED_STACK_ID) || isStackVisible(FREEFORM_WORKSPACE_STACK_ID)) {
                 // Apps and their containers are not allowed to specify an orientation while the
                 // docked or freeform stack is visible...except for the home stack/task if the
                 // docked stack is minimized and it actually set something.
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index ed1e2d9..aaf724e 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -457,8 +457,7 @@
     void registerDockedStackListener(IDockedStackListener listener) {
         mDockedStackListeners.register(listener);
         notifyDockedDividerVisibilityChanged(wasVisible());
-        notifyDockedStackExistsChanged(
-                mDisplayContent.mService.mStackIdToStack.get(DOCKED_STACK_ID) != null);
+        notifyDockedStackExistsChanged(mDisplayContent.getDockedStackIgnoringVisibility() != null);
         notifyDockedStackMinimizedChanged(mMinimizedDock, 0 /* animDuration */);
         notifyAdjustedForImeChanged(mAdjustedForIme, 0 /* animDuration */);
 
@@ -466,7 +465,7 @@
 
     void setResizeDimLayer(boolean visible, int targetStackId, float alpha) {
         mService.openSurfaceTransaction();
-        final TaskStack stack = mDisplayContent.mService.mStackIdToStack.get(targetStackId);
+        final TaskStack stack = mDisplayContent.getStackById(targetStackId);
         final TaskStack dockedStack = mDisplayContent.getDockedStackLocked();
         boolean visibleAndValid = visible && stack != null && dockedStack != null;
         if (visibleAndValid) {
@@ -544,8 +543,8 @@
         if (homeTask == null || !isWithinDisplay(homeTask)) {
             return;
         }
-        final TaskStack fullscreenStack
-                = mService.mStackIdToStack.get(FULLSCREEN_WORKSPACE_STACK_ID);
+        final TaskStack fullscreenStack =
+                mDisplayContent.getStackById(FULLSCREEN_WORKSPACE_STACK_ID);
         final boolean homeVisible = homeTask.getTopVisibleAppToken() != null;
         final boolean homeBehind = (fullscreenStack != null && fullscreenStack.isVisible())
                 || (homeStack.hasMultipleTaskWithHomeTaskNotTop());
@@ -739,7 +738,7 @@
     }
 
     private boolean animateForMinimizedDockedStack(long now) {
-        final TaskStack stack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
+        final TaskStack stack = mDisplayContent.getStackById(DOCKED_STACK_ID);
         if (!mAnimationStarted) {
             mAnimationStarted = true;
             mAnimationStartTime = now;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 0cc6c70..22abf30 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -382,6 +382,17 @@
         }
     }
 
+    TaskStack getStackById(int stackId) {
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            final DisplayContent dc = mChildren.get(i);
+            final TaskStack stack = dc.getStackById(stackId);
+            if (stack != null) {
+                return stack;
+            }
+        }
+        return null;
+    }
+
     void setSecureSurfaceState(int userId, boolean disabled) {
         forAllWindows((w) -> {
             if (w.mHasSurface && userId == UserHandle.getUserId(w.mOwnerUid)) {
@@ -535,18 +546,7 @@
         final int numDisplays = mChildren.size();
         for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
             final DisplayContent displayContent = mChildren.get(displayNdx);
-            for (i = displayContent.mExitingTokens.size() - 1; i >= 0; i--) {
-                displayContent.mExitingTokens.get(i).hasVisible = false;
-            }
-        }
-
-        for (int stackNdx = mService.mStackIdToStack.size() - 1; stackNdx >= 0; --stackNdx) {
-            // Initialize state of exiting applications.
-            final AppTokenList exitingAppTokens =
-                    mService.mStackIdToStack.valueAt(stackNdx).mExitingAppTokens;
-            for (int tokenNdx = exitingAppTokens.size() - 1; tokenNdx >= 0; --tokenNdx) {
-                exitingAppTokens.get(tokenNdx).hasVisible = false;
-            }
+            displayContent.setExitingTokensHasVisible(false);
         }
 
         mHoldScreen = null;
@@ -681,33 +681,7 @@
         // Time to remove any exiting tokens?
         for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
             final DisplayContent displayContent = mChildren.get(displayNdx);
-            ArrayList<WindowToken> exitingTokens = displayContent.mExitingTokens;
-            for (i = exitingTokens.size() - 1; i >= 0; i--) {
-                WindowToken token = exitingTokens.get(i);
-                if (!token.hasVisible) {
-                    exitingTokens.remove(i);
-                }
-            }
-        }
-
-        // Time to remove any exiting applications?
-        for (int stackNdx = mService.mStackIdToStack.size() - 1; stackNdx >= 0; --stackNdx) {
-            // Initialize state of exiting applications.
-            final AppTokenList exitingAppTokens =
-                    mService.mStackIdToStack.valueAt(stackNdx).mExitingAppTokens;
-            for (i = exitingAppTokens.size() - 1; i >= 0; i--) {
-                final AppWindowToken token = exitingAppTokens.get(i);
-                if (!token.hasVisible && !mService.mClosingApps.contains(token) &&
-                        (!token.mIsExiting || token.isEmpty())) {
-                    // Make sure there is no animation running on this token, so any windows
-                    // associated with it will be removed as soon as their animations are complete
-                    token.mAppAnimator.clearAnimation();
-                    token.mAppAnimator.animating = false;
-                    if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG,
-                            "performLayout: App token exiting now removed" + token);
-                    token.removeIfPossible();
-                }
-            }
+            displayContent.removeExistingTokensIfPossible();
         }
 
         if (wallpaperDestroyed) {
diff --git a/services/core/java/com/android/server/wm/StackWindowController.java b/services/core/java/com/android/server/wm/StackWindowController.java
new file mode 100644
index 0000000..9a6f3eb5
--- /dev/null
+++ b/services/core/java/com/android/server/wm/StackWindowController.java
@@ -0,0 +1,315 @@
+/*
+ * 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.app.RemoteAction;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Slog;
+import android.util.SparseArray;
+import com.android.server.UiThread;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.ref.WeakReference;
+import java.util.List;
+
+import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+/**
+ * Controller for the stack container. This is created by activity manager to link activity stacks
+ * to the stack container they use in window manager.
+ *
+ * Test class: {@link StackWindowControllerTests}
+ */
+public class StackWindowController
+        extends WindowContainerController<TaskStack, StackWindowListener> {
+
+    final int mStackId;
+
+    private final H mHandler;
+
+    public StackWindowController(int stackId, StackWindowListener listener,
+            int displayId, boolean onTop, Rect outBounds) {
+        this(stackId, listener, displayId, onTop, outBounds, WindowManagerService.getInstance());
+    }
+
+    @VisibleForTesting
+    public StackWindowController(int stackId, StackWindowListener listener,
+            int displayId, boolean onTop, Rect outBounds, WindowManagerService service) {
+        super(listener, service);
+        mStackId = stackId;
+        mHandler = new H(new WeakReference<>(this), service.mH.getLooper());
+
+        synchronized (mWindowMap) {
+            final DisplayContent dc = mRoot.getDisplayContent(displayId);
+            if (dc == null) {
+                throw new IllegalArgumentException("Trying to add stackId=" + stackId
+                        + " to unknown displayId=" + displayId);
+            }
+
+            final TaskStack stack = dc.addStackToDisplay(stackId, onTop);
+            stack.setController(this);
+            getRawBounds(outBounds);
+        }
+    }
+
+    @Override
+    public void removeContainer() {
+        synchronized (mWindowMap) {
+            if (mContainer != null) {
+                mContainer.removeIfPossible();
+                super.removeContainer();
+            }
+        }
+    }
+
+    public void reparent(int displayId, Rect outStackBounds) {
+        synchronized (mWindowMap) {
+            if (mContainer == null) {
+                throw new IllegalArgumentException("Trying to move unknown stackId=" + mStackId
+                        + " to displayId=" + displayId);
+            }
+
+            final DisplayContent targetDc = mRoot.getDisplayContent(displayId);
+            if (targetDc == null) {
+                throw new IllegalArgumentException("Trying to move stackId=" + mStackId
+                        + " to unknown displayId=" + displayId);
+            }
+
+            targetDc.moveStackToDisplay(mContainer);
+            getRawBounds(outStackBounds);
+        }
+    }
+
+    public void positionChildAt(TaskWindowContainerController child, int position, Rect bounds,
+            Configuration overrideConfig) {
+        synchronized (mWindowMap) {
+            if (DEBUG_STACK) Slog.i(TAG_WM, "positionChildAt: positioning task=" + child
+                    + " at " + position);
+            if (child.mContainer == null) {
+                if (DEBUG_STACK) Slog.i(TAG_WM,
+                        "positionChildAt: could not find task=" + this);
+                return;
+            }
+            if (mContainer == null) {
+                if (DEBUG_STACK) Slog.i(TAG_WM,
+                        "positionChildAt: could not find stack for task=" + mContainer);
+                return;
+            }
+            child.mContainer.positionAt(position, bounds, overrideConfig);
+            mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
+        }
+    }
+
+    public void positionChildAtTop(TaskWindowContainerController child, boolean includingParents) {
+        if (child == null) {
+            // TODO: Fix the call-points that cause this to happen.
+            return;
+        }
+
+        synchronized(mWindowMap) {
+            final Task childTask = child.mContainer;
+            if (childTask == null) {
+                Slog.e(TAG_WM, "positionChildAtTop: task=" + child + " not found");
+                return;
+            }
+            mContainer.positionChildAt(POSITION_TOP, childTask, includingParents);
+
+            if (mService.mAppTransition.isTransitionSet()) {
+                childTask.setSendingToBottom(false);
+            }
+            mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
+        }
+    }
+
+    public void positionChildAtBottom(TaskWindowContainerController child) {
+        if (child == null) {
+            // TODO: Fix the call-points that cause this to happen.
+            return;
+        }
+
+        synchronized(mWindowMap) {
+            final Task childTask = child.mContainer;
+            if (childTask == null) {
+                Slog.e(TAG_WM, "positionChildAtBottom: task=" + child + " not found");
+                return;
+            }
+            mContainer.positionChildAt(POSITION_BOTTOM, childTask, false /* includingParents */);
+
+            if (mService.mAppTransition.isTransitionSet()) {
+                childTask.setSendingToBottom(true);
+            }
+            mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
+        }
+    }
+
+    /**
+     * Re-sizes a stack and its containing tasks.
+     *
+     * @param bounds New stack bounds. Passing in null sets the bounds to fullscreen.
+     * @param configs Configurations for tasks in the resized stack, keyed by task id.
+     * @param taskBounds Bounds for tasks in the resized stack, keyed by task id.
+     * @return True if the stack is now fullscreen.
+     */
+    public boolean resize(Rect bounds, SparseArray<Configuration> configs,
+            SparseArray<Rect> taskBounds, SparseArray<Rect> taskTempInsetBounds) {
+        synchronized (mWindowMap) {
+            if (mContainer == null) {
+                throw new IllegalArgumentException("resizeStack: stack " + this + " not found.");
+            }
+            // We might trigger a configuration change. Save the current task bounds for freezing.
+            mContainer.prepareFreezingTaskBounds();
+            if (mContainer.setBounds(bounds, configs, taskBounds, taskTempInsetBounds)
+                    && mContainer.isVisible()) {
+                mContainer.getDisplayContent().setLayoutNeeded();
+                mService.mWindowPlacerLocked.performSurfacePlacement();
+            }
+            return mContainer.getRawFullscreen();
+        }
+    }
+
+    // TODO: This and similar methods should be separated into PinnedStackWindowContainerController
+    public void animateResizePinnedStack(final Rect bounds, final int animationDuration) {
+        synchronized (mWindowMap) {
+            if (mContainer == null) {
+                throw new IllegalArgumentException("Pinned stack container not found :(");
+            }
+            final Rect originalBounds = new Rect();
+            mContainer.getBounds(originalBounds);
+            mContainer.setAnimatingBounds(bounds);
+            UiThread.getHandler().post(new Runnable() {
+                @Override
+                public void run() {
+                    mService.mBoundsAnimationController.animateBounds(
+                            mContainer, originalBounds, bounds, animationDuration);
+                }
+            });
+        }
+    }
+
+    /** Sets the current picture-in-picture aspect ratio. */
+    public void setPictureInPictureAspectRatio(float aspectRatio) {
+        synchronized (mWindowMap) {
+            if (!mService.mSupportsPictureInPicture || mContainer == null) {
+                return;
+            }
+
+            final int displayId = mContainer.getDisplayContent().getDisplayId();
+            final Rect toBounds = mService.getPictureInPictureBounds(displayId, aspectRatio);
+            animateResizePinnedStack(toBounds, -1 /* duration */);
+        }
+    }
+
+    public void getStackDockedModeBounds(Rect outBounds, boolean ignoreVisibility) {
+        synchronized (mWindowMap) {
+            if (mContainer != null) {
+                mContainer.getStackDockedModeBoundsLocked(outBounds, ignoreVisibility);
+                return;
+            }
+            outBounds.setEmpty();
+        }
+    }
+
+    public void prepareFreezingTaskBounds() {
+        synchronized (mWindowMap) {
+            if (mContainer == null) {
+                throw new IllegalArgumentException("prepareFreezingTaskBounds: stack " + this
+                        + " not found.");
+            }
+            mContainer.prepareFreezingTaskBounds();
+        }
+    }
+
+    /** Sets the current picture-in-picture actions. */
+    public void setPictureInPictureActions(List<RemoteAction> actions) {
+        synchronized (mWindowMap) {
+            if (!mService.mSupportsPictureInPicture || mContainer == null) {
+                return;
+            }
+
+            mContainer.getDisplayContent().getPinnedStackController().setActions(actions);
+        }
+    }
+
+    private void getRawBounds(Rect outBounds) {
+        if (mContainer.getRawFullscreen()) {
+            outBounds.setEmpty();
+        } else {
+            mContainer.getRawBounds(outBounds);
+        }
+    }
+
+    public void getBounds(Rect outBounds) {
+        synchronized (mWindowMap) {
+            if (mContainer != null) {
+                mContainer.getBounds(outBounds);
+                return;
+            }
+            outBounds.setEmpty();
+        }
+    }
+
+    public Rect getBoundsForNewConfiguration() {
+        synchronized(mWindowMap) {
+            final Rect outBounds = new Rect();
+            mContainer.getBoundsForNewConfiguration(outBounds);
+            return outBounds;
+        }
+    }
+
+    void requestResize(Rect bounds) {
+        mHandler.obtainMessage(H.REQUEST_RESIZE, bounds).sendToTarget();
+    }
+
+    @Override
+    public String toString() {
+        return "{StackWindowController stackId=" + mStackId + "}";
+    }
+
+    private static final class H extends Handler {
+
+        static final int REQUEST_RESIZE = 0;
+
+        private final WeakReference<StackWindowController> mController;
+
+        H(WeakReference<StackWindowController> controller, Looper looper) {
+            super(looper);
+            mController = controller;
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            final StackWindowController controller = mController.get();
+            final StackWindowListener listener = (controller != null)
+                    ? controller.mListener : null;
+            if (listener == null) {
+                return;
+            }
+            switch (msg.what) {
+                case REQUEST_RESIZE:
+                    listener.requestResize((Rect) msg.obj);
+                    break;
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/StackWindowListener.java b/services/core/java/com/android/server/wm/StackWindowListener.java
new file mode 100644
index 0000000..c763c17
--- /dev/null
+++ b/services/core/java/com/android/server/wm/StackWindowListener.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2017 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.graphics.Rect;
+
+/**
+ * Interface used by the creator of {@link StackWindowController} to listen to changes with
+ * the stack container.
+ */
+public interface StackWindowListener extends WindowContainerListener {
+
+    /** Called when the stack container would like its controller to resize. */
+    void requestResize(Rect bounds);
+}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index c5ed6ec..d96e1ef 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -27,7 +27,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_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.WindowManagerService.H.RESIZE_TASK;
 
 import android.app.ActivityManager.StackId;
 import android.app.ActivityManager.TaskDescription;
@@ -567,11 +566,10 @@
 
         displayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
         if (setBounds(mTmpRect2, getOverrideConfiguration()) != BOUNDS_CHANGE_NONE) {
-            // Post message to inform activity manager of the bounds change simulating a one-way
-            // call. We do this to prevent a deadlock between window manager lock and activity
-            // manager lock been held.
-            mService.mH.obtainMessage(RESIZE_TASK, mTaskId,
-                    RESIZE_MODE_SYSTEM_SCREEN_ROTATION, mBounds).sendToTarget();
+            final TaskWindowContainerController controller = getController();
+            if (controller != null) {
+                controller.requestResize(mBounds, RESIZE_MODE_SYSTEM_SCREEN_ROTATION);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 53292bb..d3eae8c 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -35,17 +35,12 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.H.RESIZE_STACK;
 import static com.android.server.wm.WindowManagerService.LAYER_OFFSET_DIM;
 
 import android.app.ActivityManager.StackId;
-import android.app.IActivityManager;
 import android.content.res.Configuration;
-import android.graphics.Point;
-import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.Region;
-import android.os.Debug;
 import android.os.RemoteException;
 import android.util.EventLog;
 import android.util.Slog;
@@ -53,11 +48,9 @@
 import android.view.DisplayInfo;
 import android.view.Surface;
 
-import android.view.WindowManagerPolicy;
 import com.android.internal.policy.DividerSnapAlgorithm;
 import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
 import com.android.internal.policy.DockedDividerUtils;
-import com.android.internal.policy.PipSnapAlgorithm;
 import com.android.server.EventLogTags;
 
 import java.io.PrintWriter;
@@ -206,14 +199,6 @@
         }
     }
 
-    boolean isFullscreenBounds(Rect bounds) {
-        if (mDisplayContent == null || bounds == null) {
-            return true;
-        }
-        mDisplayContent.getLogicalDisplayRect(mTmpRect);
-        return mTmpRect.equals(bounds);
-    }
-
     /**
      * Overrides the adjusted bounds, i.e. sets temporary layout bounds which are different from
      * the normal task bounds.
@@ -658,7 +643,7 @@
 
         final Rect oldBounds = new Rect(mBounds);
         Rect bounds = null;
-        final TaskStack dockedStack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
+        final TaskStack dockedStack = dc.getDockedStackIgnoringVisibility();
         if (mStackId == DOCKED_STACK_ID
                 || (dockedStack != null && StackId.isResizeableByDockedStack(mStackId)
                         && !dockedStack.fillsParent())) {
@@ -697,7 +682,7 @@
             return;
         }
 
-        final TaskStack dockedStack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
+        final TaskStack dockedStack = mDisplayContent.getDockedStackIgnoringVisibility();
         if (dockedStack == null) {
             // Not sure why you are calling this method when there is no docked stack...
             throw new IllegalStateException(
@@ -805,8 +790,12 @@
 
         final Rect bounds = new Rect();
         getStackDockedModeBoundsLocked(bounds, true /*ignoreVisibility*/);
-        mService.mH.obtainMessage(RESIZE_STACK, DOCKED_STACK_ID,
-                1 /*allowResizeInDockedMode*/, bounds).sendToTarget();
+        getController().requestResize(bounds);
+    }
+
+    @Override
+    StackWindowController getController() {
+        return (StackWindowController) super.getController();
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/TaskWindowContainerController.java b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
index fbc3389..11667c0 100644
--- a/services/core/java/com/android/server/wm/TaskWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
@@ -27,6 +27,8 @@
 import android.util.Slog;
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.lang.ref.WeakReference;
+
 import static com.android.server.EventLogTags.WM_TASK_CREATED;
 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
 import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
@@ -43,43 +45,38 @@
 public class TaskWindowContainerController
         extends WindowContainerController<Task, TaskWindowContainerListener> {
 
-    private static final int REPORT_SNAPSHOT_CHANGED = 0;
-
     private final int mTaskId;
-
-    private final Handler mHandler = new Handler(Looper.getMainLooper()) {
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case REPORT_SNAPSHOT_CHANGED:
-                    if (mListener != null) {
-                        mListener.onSnapshotChanged((TaskSnapshot) msg.obj);
-                    }
-                    break;
-            }
-        }
-    };
+    private final H mHandler;
 
     public TaskWindowContainerController(int taskId, TaskWindowContainerListener listener,
-            int stackId, int userId, Rect bounds, Configuration overrideConfig, int resizeMode,
-            boolean supportsPictureInPicture, boolean homeTask, boolean isOnTopLauncher,
-            boolean toTop, boolean showForAllUsers, TaskDescription taskDescription) {
-        super(listener, WindowManagerService.getInstance());
+            StackWindowController stackController, int userId, Rect bounds,
+            Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture,
+            boolean homeTask, boolean isOnTopLauncher, boolean toTop, boolean showForAllUsers,
+            TaskDescription taskDescription) {
+        this(taskId, listener, stackController, userId, bounds, overrideConfig, resizeMode,
+                supportsPictureInPicture, homeTask, isOnTopLauncher, toTop, showForAllUsers,
+                taskDescription, WindowManagerService.getInstance());
+    }
+
+    public TaskWindowContainerController(int taskId, TaskWindowContainerListener listener,
+            StackWindowController stackController, int userId, Rect bounds,
+            Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture,
+            boolean homeTask, boolean isOnTopLauncher, boolean toTop, boolean showForAllUsers,
+            TaskDescription taskDescription, WindowManagerService service) {
+        super(listener, service);
         mTaskId = taskId;
+        mHandler = new H(new WeakReference<>(this), service.mH.getLooper());
 
         synchronized(mWindowMap) {
             if (DEBUG_STACK) Slog.i(TAG_WM, "TaskWindowContainerController: taskId=" + taskId
-                    + " stackId=" + stackId + " bounds=" + bounds);
+                    + " stack=" + stackController + " bounds=" + bounds);
 
-            // TODO: Pass controller for the stack to get the container object when stack is
-            // switched to use controller.
-            final TaskStack stack = mService.mStackIdToStack.get(stackId);
+            final TaskStack stack = stackController.mContainer;
             if (stack == null) {
-                throw new IllegalArgumentException("TaskWindowContainerController: invalid stackId="
-                        + stackId);
+                throw new IllegalArgumentException("TaskWindowContainerController: invalid stack="
+                        + stackController);
             }
-            EventLog.writeEvent(WM_TASK_CREATED, taskId, stackId);
+            EventLog.writeEvent(WM_TASK_CREATED, taskId, stack.mStackId);
             final Task task = createTask(taskId, stack, userId, bounds, overrideConfig, resizeMode,
                     supportsPictureInPicture, homeTask, isOnTopLauncher, taskDescription);
             final int position = toTop ? POSITION_TOP : POSITION_BOTTOM;
@@ -124,21 +121,22 @@
         }
     }
 
-    public void reparent(int stackId, int position) {
+    public void reparent(StackWindowController stackController, int position) {
         synchronized (mWindowMap) {
             if (DEBUG_STACK) Slog.i(TAG_WM, "reparent: moving taskId=" + mTaskId
-                    + " to stackId=" + stackId + " at " + position);
+                    + " to stack=" + stackController + " at " + position);
             if (mContainer == null) {
                 if (DEBUG_STACK) Slog.i(TAG_WM,
                         "reparent: could not find taskId=" + mTaskId);
                 return;
             }
-            final TaskStack stack = mService.mStackIdToStack.get(stackId);
+            final TaskStack stack = stackController.mContainer;
             if (stack == null) {
-                throw new IllegalArgumentException("reparent: could not find stackId=" + stackId);
+                throw new IllegalArgumentException("reparent: could not find stack="
+                        + stackController);
             }
             mContainer.reparent(stack, position);
-            mService.mWindowPlacerLocked.performSurfacePlacement();
+            mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
         }
     }
 
@@ -158,68 +156,11 @@
             }
 
             if (mContainer.resizeLocked(bounds, overrideConfig, forced) && relayout) {
-                mContainer.getDisplayContent().setLayoutNeeded();
-                mService.mWindowPlacerLocked.performSurfacePlacement();
+                mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
             }
         }
     }
 
-    // TODO: Move to positionChildAt() in stack controller once we have a stack controller.
-    public void positionAt(int position, Rect bounds, Configuration overrideConfig) {
-        synchronized (mWindowMap) {
-            if (DEBUG_STACK) Slog.i(TAG_WM, "positionChildAt: positioning taskId=" + mTaskId
-                    + " at " + position);
-            if (mContainer == null) {
-                if (DEBUG_STACK) Slog.i(TAG_WM,
-                        "positionAt: could not find taskId=" + mTaskId);
-                return;
-            }
-            final TaskStack stack = mContainer.mStack;
-            if (stack == null) {
-                if (DEBUG_STACK) Slog.i(TAG_WM,
-                        "positionAt: could not find stack for task=" + mContainer);
-                return;
-            }
-            mContainer.positionAt(position, bounds, overrideConfig);
-            final DisplayContent displayContent = stack.getDisplayContent();
-            displayContent.setLayoutNeeded();
-            mService.mWindowPlacerLocked.performSurfacePlacement();
-        }
-    }
-
-    // TODO: Replace with moveChildToTop in stack controller?
-    public void moveToTop(boolean includingParents) {
-        synchronized(mWindowMap) {
-            if (mContainer == null) {
-                Slog.e(TAG_WM, "moveToTop: taskId=" + mTaskId + " not found");
-                return;
-            }
-            final TaskStack stack = mContainer.mStack;
-            stack.positionChildAt(POSITION_TOP, mContainer, includingParents);
-
-            if (mService.mAppTransition.isTransitionSet()) {
-                mContainer.setSendingToBottom(false);
-            }
-            stack.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
-        }
-    }
-
-    // TODO: Replace with moveChildToBottom in stack controller?
-    public void moveToBottom() {
-        synchronized(mWindowMap) {
-            if (mContainer == null) {
-                Slog.e(TAG_WM, "moveTaskToBottom: taskId=" + mTaskId + " not found");
-                return;
-            }
-            final TaskStack stack = mContainer.mStack;
-            stack.positionChildAt(POSITION_BOTTOM, mContainer, false /* includingParents */);
-            if (mService.mAppTransition.isTransitionSet()) {
-                mContainer.setSendingToBottom(true);
-            }
-            stack.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
-        }
-    }
-
     public void getBounds(Rect bounds) {
         synchronized (mWindowMap) {
             if (mContainer != null) {
@@ -276,11 +217,46 @@
     }
 
     void reportSnapshotChanged(TaskSnapshot snapshot) {
-        mHandler.obtainMessage(REPORT_SNAPSHOT_CHANGED, snapshot).sendToTarget();
+        mHandler.obtainMessage(H.REPORT_SNAPSHOT_CHANGED, snapshot).sendToTarget();
+    }
+
+    void requestResize(Rect bounds, int resizeMode) {
+        mHandler.obtainMessage(H.REQUEST_RESIZE, resizeMode, 0, bounds).sendToTarget();
     }
 
     @Override
     public String toString() {
         return "{TaskWindowContainerController taskId=" + mTaskId + "}";
     }
+
+    private static final class H extends Handler {
+
+        static final int REPORT_SNAPSHOT_CHANGED = 0;
+        static final int REQUEST_RESIZE = 1;
+
+        private final WeakReference<TaskWindowContainerController> mController;
+
+        H(WeakReference<TaskWindowContainerController> controller, Looper looper) {
+            super(looper);
+            mController = controller;
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            final TaskWindowContainerController controller = mController.get();
+            final TaskWindowContainerListener listener = (controller != null)
+                    ? controller.mListener : null;
+            if (listener == null) {
+                return;
+            }
+            switch (msg.what) {
+                case REPORT_SNAPSHOT_CHANGED:
+                    listener.onSnapshotChanged((TaskSnapshot) msg.obj);
+                    break;
+                case REQUEST_RESIZE:
+                    listener.requestResize((Rect) msg.obj, msg.arg1);
+                    break;
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/TaskWindowContainerListener.java b/services/core/java/com/android/server/wm/TaskWindowContainerListener.java
index 61b202d..af67de3 100644
--- a/services/core/java/com/android/server/wm/TaskWindowContainerListener.java
+++ b/services/core/java/com/android/server/wm/TaskWindowContainerListener.java
@@ -17,14 +17,17 @@
 package com.android.server.wm;
 
 import android.app.ActivityManager.TaskSnapshot;
+import android.graphics.Rect;
 
 /**
- * Interface used by the creator of the controller to listen to changes with the container.
+ * Interface used by the creator of {@link TaskWindowContainerController} to listen to changes with
+ * the task container.
  */
 public interface TaskWindowContainerListener extends WindowContainerListener {
 
-    /**
-     * Called when the snapshot of this task has changed.
-     */
+    /** Called when the snapshot of this task has changed. */
     void onSnapshotChanged(TaskSnapshot snapshot);
+
+    /** Called when the task container would like its controller to resize. */
+    void requestResize(Rect bounds, int resizeMode);
 }
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index feceb8e..c32e689 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -447,7 +447,7 @@
 
     private void findWallpaperTarget(DisplayContent dc) {
         mFindResults.reset();
-        if (mService.isStackVisibleLocked(FREEFORM_WORKSPACE_STACK_ID)) {
+        if (dc.isStackVisible(FREEFORM_WORKSPACE_STACK_ID)) {
             // In freeform mode we set the wallpaper as its own target, so we don't need an
             // additional window to make it visible.
             mFindResults.setUseTopWallpaperAsTarget(true);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c8f4bd2..1987f90 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -706,12 +706,7 @@
 
     final WindowAnimator mAnimator;
 
-    private final BoundsAnimationController mBoundsAnimationController;
-
-    /** All of the TaskStacks in the window manager, unordered. For an ordered list call
-     * DisplayContent.getStacks(). */
-    // TODO: Don't believe this is needed with the WindowContainer model.
-    SparseArray<TaskStack> mStackIdToStack = new SparseArray<>();
+    final BoundsAnimationController mBoundsAnimationController;
 
     private final PointerEventDispatcher mPointerEventDispatcher;
 
@@ -2573,16 +2568,6 @@
         }
     }
 
-    @Override
-    public Rect getBoundsForNewConfiguration(int stackId) {
-        synchronized(mWindowMap) {
-            final TaskStack stack = mStackIdToStack.get(stackId);
-            final Rect outBounds = new Rect();
-            stack.getBoundsForNewConfiguration(outBounds);
-            return outBounds;
-        }
-    }
-
     void setFocusTaskRegionLocked() {
         final Task focusedTask = mFocusedApp != null ? mFocusedApp.mTask : null;
         if (focusedTask != null) {
@@ -2827,11 +2812,6 @@
         }
     }
 
-    boolean isStackVisibleLocked(int stackId) {
-        final TaskStack stack = mStackIdToStack.get(stackId);
-        return (stack != null && stack.isVisible());
-    }
-
     public void setDockedStackCreateState(int mode, Rect bounds) {
         synchronized (mWindowMap) {
             setDockedStackCreateStateLocked(mode, bounds);
@@ -2880,17 +2860,19 @@
             }
 
             final Rect stackBounds;
-            final DisplayContent displayContent;
-            final TaskStack stack = mStackIdToStack.get(PINNED_STACK_ID);
+            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+            if (displayContent == null) {
+                return null;
+            }
+
+            final TaskStack stack = displayContent.getStackById(PINNED_STACK_ID);
             if (stack != null) {
                 // If the stack exists, then use its final bounds to calculate the new aspect ratio
                 // bounds.
-                displayContent = stack.getDisplayContent();
                 stackBounds = new Rect();
                 stack.getAnimatingBounds(stackBounds);
             } else {
                 // Otherwise, just calculate the aspect ratio bounds from the default bounds
-                displayContent = mRoot.getDisplayContent(displayId);
                 stackBounds = displayContent.getPinnedStackController().getDefaultBounds();
             }
             return displayContent.getPinnedStackController().getAspectRatioBounds(stackBounds,
@@ -2898,119 +2880,10 @@
         }
     }
 
-    /**
-     * Sets the current picture-in-picture aspect ratio.
-     */
-    public void setPictureInPictureAspectRatio(float aspectRatio) {
-        synchronized (mWindowMap) {
-            final TaskStack stack = mStackIdToStack.get(PINNED_STACK_ID);
-            if (!mSupportsPictureInPicture || stack == null) {
-                return;
-            }
-
-            final int displayId = stack.getDisplayContent().getDisplayId();
-            final Rect toBounds = getPictureInPictureBounds(displayId, aspectRatio);
-            animateResizePinnedStack(toBounds, -1 /* duration */);
-        }
-    }
-
-    /**
-     * Sets the current picture-in-picture actions.
-     */
-    public void setPictureInPictureActions(List<RemoteAction> actions) {
-        synchronized (mWindowMap) {
-            final TaskStack stack = mStackIdToStack.get(PINNED_STACK_ID);
-            if (!mSupportsPictureInPicture || stack == null) {
-                return;
-            }
-
-            stack.getDisplayContent().getPinnedStackController().setActions(actions);
-        }
-    }
-
-    /**
-     * Place a TaskStack on a DisplayContent. Will create a new TaskStack if none is found with
-     * specified stackId.
-     * @param stackId The unique identifier of the new stack.
-     * @param displayId The unique identifier of the DisplayContent.
-     * @param onTop If true the stack will be place at the top of the display,
-     *              else at the bottom.
-     * @return The bounds that the stack has after adding. null means fullscreen.
-     */
-    public Rect addStackToDisplay(int stackId, int displayId, boolean onTop) {
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized (mWindowMap) {
-                final DisplayContent dc = mRoot.getDisplayContent(displayId);
-                if (dc == null) {
-                    throw new IllegalArgumentException("Trying to add stackId=" + stackId
-                            + " to unknown displayId=" + displayId);
-                }
-
-                return dc.addStackToDisplay(stackId, onTop);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    /**
-     * Move a TaskStack from current DisplayContent to specified one.
-     * @param stackId The unique identifier of the new stack.
-     * @param displayId The unique identifier of the new display.
-     */
-    public Rect moveStackToDisplay(int stackId, int displayId) {
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized (mWindowMap) {
-                TaskStack stack = mStackIdToStack.get(stackId);
-                if (stack == null) {
-                    throw new IllegalArgumentException("Trying to move unknown stackId=" + stackId
-                            + " to displayId=" + displayId);
-                }
-
-                final DisplayContent targetDisplayContent = mRoot.getDisplayContent(displayId);
-                if (targetDisplayContent == null) {
-                    throw new IllegalArgumentException("Trying to move stackId=" + stackId
-                            + " to unknown displayId=" + displayId);
-                }
-
-                return targetDisplayContent.moveStackToDisplay(stack);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    /**
-     * Remove a TaskStack completely.
-     * @param stackId The unique identifier of the stack.
-     */
-    public void removeStack(int stackId) {
-        synchronized (mWindowMap) {
-            final TaskStack stack = mStackIdToStack.get(stackId);
-            if (stack != null) {
-                stack.removeIfPossible();
-                mStackIdToStack.remove(stackId);
-            }
-        }
-    }
-
-    public void getStackDockedModeBounds(int stackId, Rect bounds, boolean ignoreVisibility) {
-        synchronized (mWindowMap) {
-            final TaskStack stack = mStackIdToStack.get(stackId);
-            if (stack != null) {
-                stack.getStackDockedModeBoundsLocked(bounds, ignoreVisibility);
-                return;
-            }
-            bounds.setEmpty();
-        }
-    }
-
     @Override
     public void getStackBounds(int stackId, Rect bounds) {
         synchronized (mWindowMap) {
-            final TaskStack stack = mStackIdToStack.get(stackId);
+            final TaskStack stack = mRoot.getStackById(stackId);
             if (stack != null) {
                 stack.getBounds(bounds);
                 return;
@@ -3035,43 +2908,6 @@
     }
 
     /**
-     * Re-sizes a stack and its containing tasks.
-     * @param stackId Id of stack to resize.
-     * @param bounds New stack bounds. Passing in null sets the bounds to fullscreen.
-     * @param configs Configurations for tasks in the resized stack, keyed by task id.
-     * @param taskBounds Bounds for tasks in the resized stack, keyed by task id.
-     * @return True if the stack is now fullscreen.
-     * */
-    public boolean resizeStack(int stackId, Rect bounds,
-            SparseArray<Configuration> configs, SparseArray<Rect> taskBounds,
-            SparseArray<Rect> taskTempInsetBounds) {
-        synchronized (mWindowMap) {
-            final TaskStack stack = mStackIdToStack.get(stackId);
-            if (stack == null) {
-                throw new IllegalArgumentException("resizeStack: stackId " + stackId
-                        + " not found.");
-            }
-            if (stack.setBounds(bounds, configs, taskBounds, taskTempInsetBounds)
-                    && stack.isVisible()) {
-                stack.getDisplayContent().setLayoutNeeded();
-                mWindowPlacerLocked.performSurfacePlacement();
-            }
-            return stack.getRawFullscreen();
-        }
-    }
-
-    public void prepareFreezingTaskBounds(int stackId) {
-        synchronized (mWindowMap) {
-            final TaskStack stack = mStackIdToStack.get(stackId);
-            if (stack == null) {
-                throw new IllegalArgumentException("prepareFreezingTaskBounds: stackId " + stackId
-                        + " not found.");
-            }
-            stack.prepareFreezingTaskBounds();
-        }
-    }
-
-    /**
      * Starts deferring layout passes. Useful when doing multiple changes but to optimize
      * performance, only one layout pass should be done. This can be called multiple times, and
      * layouting will be resumed once the last caller has called {@link #continueSurfaceLayout}
@@ -3505,8 +3341,9 @@
 
             // Notify whether the docked stack exists for the current user
             final DisplayContent displayContent = getDefaultDisplayContentLocked();
-            displayContent.mDividerControllerLocked
-                    .notifyDockedStackExistsChanged(hasDockedTasksForUser(newUserId));
+            final TaskStack stack = displayContent.getDockedStackIgnoringVisibility();
+            displayContent.mDividerControllerLocked.notifyDockedStackExistsChanged(
+                    stack != null && stack.hasTaskForUser(newUserId));
 
             // If the display is already prepared, update the density.
             // Otherwise, we'll update it when it's prepared.
@@ -3519,15 +3356,6 @@
         }
     }
 
-    /** Returns whether there is a docked task for the current user. */
-    boolean hasDockedTasksForUser(int userId) {
-        final TaskStack stack = mStackIdToStack.get(DOCKED_STACK_ID);
-        if (stack == null) {
-            return false;
-        }
-        return stack.hasTaskForUser(userId);
-    }
-
     /* Called by WindowState */
     boolean isCurrentProfileLocked(int userId) {
         if (userId == mCurrentUserId) return true;
@@ -5417,8 +5245,6 @@
 
         public static final int UPDATE_DOCKED_STACK_DIVIDER = 41;
 
-        public static final int RESIZE_STACK = 42;
-        public static final int RESIZE_TASK = 43;
         public static final int TEAR_DOWN_DRAG_AND_DROP_INPUT = 44;
 
         public static final int WINDOW_REPLACEMENT_TIMEOUT = 46;
@@ -5844,23 +5670,6 @@
                     }
                 }
                 break;
-                case RESIZE_TASK: {
-                    try {
-                        mActivityManager.resizeTask(msg.arg1, (Rect) msg.obj, msg.arg2);
-                    } catch (RemoteException e) {
-                        // This will not happen since we are in the same process.
-                    }
-                }
-                break;
-                case RESIZE_STACK: {
-                    try {
-                        mActivityManager.resizeStack(
-                                msg.arg1, (Rect) msg.obj, msg.arg2 == 1, false, false, -1);
-                    } catch (RemoteException e) {
-                        // This will not happen since we are in the same process.
-                    }
-                }
-                break;
                 case WINDOW_REPLACEMENT_TIMEOUT: {
                     synchronized (mWindowMap) {
                         for (int i = mWindowReplacementTimeouts.size() - 1; i >= 0; i--) {
@@ -7646,26 +7455,6 @@
         }
     }
 
-    public void animateResizePinnedStack(final Rect bounds, final int animationDuration) {
-        synchronized (mWindowMap) {
-            final TaskStack stack = mStackIdToStack.get(PINNED_STACK_ID);
-            if (stack == null) {
-                Slog.w(TAG, "animateResizePinnedStack: stackId " + PINNED_STACK_ID + " not found.");
-                return;
-            }
-            final Rect originalBounds = new Rect();
-            stack.getBounds(originalBounds);
-            stack.setAnimatingBounds(bounds);
-            UiThread.getHandler().post(new Runnable() {
-                @Override
-                public void run() {
-                    mBoundsAnimationController.animateBounds(
-                            stack, originalBounds, bounds, animationDuration);
-                }
-            });
-        }
-    }
-
     public void setForceResizableTasks(boolean forceResizableTasks) {
         synchronized (mWindowMap) {
             mForceResizableTasks = forceResizableTasks;
@@ -8123,7 +7912,8 @@
         @Override
         public boolean isStackVisible(int stackId) {
             synchronized (mWindowMap) {
-                return WindowManagerService.this.isStackVisibleLocked(stackId);
+                final DisplayContent dc = getDefaultDisplayContentLocked();
+                return dc.isStackVisible(stackId);
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 10aebe6..13358da8 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2304,6 +2304,7 @@
                     final WindowState win = mService.windowForClientLocked(mSession, mClient, false);
                     Slog.i(TAG, "WIN DEATH: " + win);
                     if (win != null) {
+                        final DisplayContent dc = getDisplayContent();
                         if (win.mAppToken != null && win.mAppToken.findMainWindow() == win) {
                             mService.mTaskSnapshotController.onAppDied(win.mAppToken);
                         }
@@ -2313,7 +2314,7 @@
                             // just in case they have the divider at an unstable position. Better
                             // also reset drag resizing state, because the owner can't do it
                             // anymore.
-                            final TaskStack stack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
+                            final TaskStack stack = dc.getDockedStackIgnoringVisibility();
                             if (stack != null) {
                                 stack.resetDockedStackToMiddle();
                             }