Made Task.mAppTokens private scoped

Also, remove TaskStack.getTask() method.

Bug: 30060889
Change-Id: I1ed9710ff630b390d28e6f2146db4202e2bc860b
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 9d29a22..d78aa32 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -22,6 +22,8 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
+import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
@@ -64,7 +66,7 @@
  * Version of WindowToken that is specifically for a particular application (or
  * really activity) that is displaying windows.
  */
-class AppWindowToken extends WindowToken {
+class AppWindowToken extends WindowToken implements WindowManagerService.AppFreezeListener {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "AppWindowToken" : TAG_WM;
 
     // Non-null only for application tokens.
@@ -375,6 +377,14 @@
         }
     }
 
+    @Override
+    boolean checkCompleteDeferredRemoval() {
+        if (mIsExiting) {
+            removeIfPossible();
+        }
+        return super.checkCompleteDeferredRemoval();
+    }
+
     void clearAnimatingFlags() {
         boolean wallpaperMightChange = false;
         for (int i = mChildren.size() - 1; i >= 0; i--) {
@@ -830,6 +840,7 @@
         if (!hiddenRequested) {
             if (!mAppAnimator.freezingScreen) {
                 mAppAnimator.freezingScreen = true;
+                mService.registerAppFreezeListener(this);
                 mAppAnimator.lastFreezeDuration = 0;
                 mService.mAppsFreezingScreen++;
                 if (mService.mAppsFreezingScreen == 1) {
@@ -860,6 +871,7 @@
         if (force || unfrozeWindows) {
             if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "No longer freezing: " + this);
             mAppAnimator.freezingScreen = false;
+            mService.unregisterAppFreezeListener(this);
             mAppAnimator.lastFreezeDuration =
                     (int)(SystemClock.elapsedRealtime() - mService.mDisplayFreezeTime);
             mService.mAppsFreezingScreen--;
@@ -873,6 +885,12 @@
         }
     }
 
+    @Override
+    public void onAppFreezeTimeout() {
+        Slog.w(TAG_WM, "Force clearing freeze: " + this);
+        stopFreezingScreen(true, true);
+    }
+
     boolean transferStartingWindow(IBinder transferFrom) {
         final AppWindowToken fromToken = mService.findAppWindowToken(transferFrom);
         if (fromToken == null) {
@@ -1002,6 +1020,84 @@
     }
 
     @Override
+    void checkAppWindowsReadyToShow(int displayId) {
+        if (allDrawn == mAppAnimator.allDrawn) {
+            return;
+        }
+
+        mAppAnimator.allDrawn = allDrawn;
+        if (!allDrawn) {
+            return;
+        }
+
+        // The token has now changed state to having all windows shown...  what to do, what to do?
+        if (mAppAnimator.freezingScreen) {
+            mAppAnimator.showAllWindowsLocked();
+            stopFreezingScreen(false, true);
+            if (DEBUG_ORIENTATION) Slog.i(TAG,
+                    "Setting mOrientationChangeComplete=true because wtoken " + this
+                    + " numInteresting=" + numInterestingWindows + " numDrawn=" + numDrawnWindows);
+            // This will set mOrientationChangeComplete and cause a pass through layout.
+            setAppLayoutChanges(FINISH_LAYOUT_REDO_WALLPAPER,
+                    "checkAppWindowsReadyToShow: freezingScreen", displayId);
+        } else {
+            setAppLayoutChanges(FINISH_LAYOUT_REDO_ANIM, "checkAppWindowsReadyToShow", displayId);
+
+            // We can now show all of the drawn windows!
+            if (!mService.mOpeningApps.contains(this)) {
+                mService.mAnimator.orAnimating(mAppAnimator.showAllWindowsLocked());
+            }
+        }
+    }
+
+    @Override
+    void updateAllDrawn(int displayId) {
+        final DisplayContent displayContent = mService.getDisplayContentLocked(displayId);
+
+        if (!allDrawn) {
+            final int numInteresting = numInterestingWindows;
+            if (numInteresting > 0 && numDrawnWindows >= numInteresting) {
+                if (DEBUG_VISIBILITY) Slog.v(TAG, "allDrawn: " + this
+                        + " interesting=" + numInteresting + " drawn=" + numDrawnWindows);
+                allDrawn = true;
+                // Force an additional layout pass where
+                // WindowStateAnimator#commitFinishDrawingLocked() will call performShowLocked().
+                displayContent.layoutNeeded = true;
+                mService.mH.obtainMessage(NOTIFY_ACTIVITY_DRAWN, token).sendToTarget();
+            }
+        }
+        if (!allDrawnExcludingSaved) {
+            int numInteresting = numInterestingWindowsExcludingSaved;
+            if (numInteresting > 0 && numDrawnWindowsExcludingSaved >= numInteresting) {
+                if (DEBUG_VISIBILITY) Slog.v(TAG, "allDrawnExcludingSaved: " + this
+                        + " interesting=" + numInteresting
+                        + " drawn=" + numDrawnWindowsExcludingSaved);
+                allDrawnExcludingSaved = true;
+                displayContent.layoutNeeded = true;
+                if (isAnimatingInvisibleWithSavedSurface()
+                        && !mService.mFinishedEarlyAnim.contains(this)) {
+                    mService.mFinishedEarlyAnim.add(this);
+                }
+            }
+        }
+    }
+
+    @Override
+    void stepAppWindowsAnimation(long currentTime, int displayId) {
+        mAppAnimator.wasAnimating = mAppAnimator.animating;
+        if (mAppAnimator.stepAnimationLocked(currentTime, displayId)) {
+            mAppAnimator.animating = true;
+            mService.mAnimator.setAnimating(true);
+            mService.mAnimator.mAppWindowAnimating = true;
+        } else if (mAppAnimator.wasAnimating) {
+            // stopped animating, do one more pass through the layout
+            setAppLayoutChanges(
+                    FINISH_LAYOUT_REDO_WALLPAPER, "appToken " + this + " done", displayId);
+            if (DEBUG_ANIM) Slog.v(TAG, "updateWindowsApps...: done animating " + this);
+        }
+    }
+
+    @Override
     int rebuildWindowList(DisplayContent dc, int addIndex) {
         if (mIsExiting && !waitingForReplacement()) {
             return addIndex;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 155c46f..f764eed 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -120,9 +120,6 @@
     private final Rect mTmpRect2 = new Rect();
     private final Region mTmpRegion = new Region();
 
-    /** For gathering Task objects in order. */
-    private final ArrayList<Task> mTmpTaskHistory = new ArrayList<Task>();
-
     final WindowManagerService mService;
 
     /** Remove this display when animation on it has completed. */
@@ -137,6 +134,11 @@
     /** Used when rebuilding window list to keep track of windows that have been removed. */
     private WindowState[] mRebuildTmp = new WindowState[20];
 
+    private final TaskForResizePointSearchResult mTmpTaskForResizePointSearchResult =
+            new TaskForResizePointSearchResult();
+    private final GetWindowOnDisplaySearchResult mTmpGetWindowOnDisplaySearchResult =
+            new GetWindowOnDisplaySearchResult();
+
     /**
      * @param display May not be null.
      * @param service You know.
@@ -192,19 +194,6 @@
         return mStacks;
     }
 
-    /**
-     * Retrieve the tasks on this display in stack order from the bottommost TaskStack up.
-     * @return All the Tasks, in order, on this display.
-     */
-    ArrayList<Task> getTasks() {
-        mTmpTaskHistory.clear();
-        final int numStacks = mStacks.size();
-        for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
-            mTmpTaskHistory.addAll(mStacks.get(stackNdx).getTasks());
-        }
-        return mTmpTaskHistory;
-    }
-
     TaskStack getHomeStack() {
         if (mHomeStack == null && mDisplayId == Display.DEFAULT_DISPLAY) {
             Slog.e(TAG_WM, "getHomeStack: Returning null from this=" + this);
@@ -222,6 +211,27 @@
         return null;
     }
 
+    void checkAppWindowsReadyToShow() {
+        for (int i = mStacks.size() - 1; i >= 0; --i) {
+            final TaskStack stack = mStacks.get(i);
+            stack.checkAppWindowsReadyToShow(mDisplayId);
+        }
+    }
+
+    void updateAllDrawn() {
+        for (int i = mStacks.size() - 1; i >= 0; --i) {
+            final TaskStack stack = mStacks.get(i);
+            stack.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);
+        }
+    }
+
     void onAppTransitionDone() {
         for (int i = mStacks.size() - 1; i >= 0; --i) {
             final TaskStack stack = mStacks.get(i);
@@ -360,6 +370,19 @@
         mStacks.add(addIndex, stack);
     }
 
+    // TODO: Don't forget to switch to WC.detachChild
+    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);
@@ -375,27 +398,10 @@
 
     int taskIdFromPoint(int x, int y) {
         for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            TaskStack stack = mStacks.get(stackNdx);
-            stack.getBounds(mTmpRect);
-            if (!mTmpRect.contains(x, y) || stack.isAdjustedForMinimizedDockedStack()) {
-                continue;
-            }
-            final ArrayList<Task> tasks = stack.getTasks();
-            for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
-                final Task task = tasks.get(taskNdx);
-                final WindowState win = task.getTopVisibleAppMainWindow();
-                if (win == null) {
-                    continue;
-                }
-                // We need to use the task's dim bounds (which is derived from the visible
-                // bounds of its apps windows) for any touch-related tests. Can't use
-                // the task's original bounds because it might be adjusted to fit the
-                // content frame. For example, the presence of the IME adjusting the
-                // windows frames when the app window is the IME target.
-                task.getDimBounds(mTmpRect);
-                if (mTmpRect.contains(x, y)) {
-                    return task.mTaskId;
-                }
+            final TaskStack stack = mStacks.get(stackNdx);
+            final int taskId = stack.taskIdFromPoint(x, y);
+            if (taskId != -1) {
+                return taskId;
             }
         }
         return -1;
@@ -407,35 +413,16 @@
      */
     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);
             if (!StackId.isTaskResizeAllowed(stack.mStackId)) {
-                break;
+                return null;
             }
-            final ArrayList<Task> tasks = stack.getTasks();
-            for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
-                final Task task = tasks.get(taskNdx);
-                if (task.isFullscreen()) {
-                    return null;
-                }
 
-                // We need to use the task's dim bounds (which is derived from the visible
-                // bounds of its apps windows) for any touch-related tests. Can't use
-                // the task's original bounds because it might be adjusted to fit the
-                // content frame. One example is when the task is put to top-left quadrant,
-                // the actual visible area would not start at (0,0) after it's adjusted
-                // for the status bar.
-                task.getDimBounds(mTmpRect);
-                mTmpRect.inset(-delta, -delta);
-                if (mTmpRect.contains(x, y)) {
-                    mTmpRect.inset(delta, delta);
-                    if (!mTmpRect.contains(x, y)) {
-                        return task;
-                    }
-                    // User touched inside the task. No need to look further,
-                    // focus transfer will be handled in ACTION_UP.
-                    return null;
-                }
+            stack.findTaskForResizePoint(x, y, delta, mTmpTaskForResizePointSearchResult);
+            if (mTmpTaskForResizePointSearchResult.searchDone) {
+                return mTmpTaskForResizePointSearchResult.taskForResize;
             }
         }
         return null;
@@ -444,54 +431,16 @@
     void setTouchExcludeRegion(Task focusedTask) {
         mTouchExcludeRegion.set(mBaseDisplayRect);
         final int delta = mService.dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics);
-        boolean addBackFocusedTask = false;
+        mTmpRect2.setEmpty();
         for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            TaskStack stack = mStacks.get(stackNdx);
-            final ArrayList<Task> tasks = stack.getTasks();
-            for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
-                final Task task = tasks.get(taskNdx);
-                AppWindowToken token = task.getTopVisibleAppToken();
-                if (token == null || !token.isVisible()) {
-                    continue;
-                }
-
-                /**
-                 * Exclusion region is the region that TapDetector doesn't care about.
-                 * Here we want to remove all non-focused tasks from the exclusion region.
-                 * We also remove the outside touch area for resizing for all freeform
-                 * tasks (including the focused).
-                 *
-                 * We save the focused task region once we find it, and add it back at the end.
-                 */
-
-                task.getDimBounds(mTmpRect);
-
-                if (task == focusedTask) {
-                    addBackFocusedTask = true;
-                    mTmpRect2.set(mTmpRect);
-                }
-
-                final boolean isFreeformed = task.inFreeformWorkspace();
-                if (task != focusedTask || isFreeformed) {
-                    if (isFreeformed) {
-                        // If the task is freeformed, enlarge the area to account for outside
-                        // touch area for resize.
-                        mTmpRect.inset(-delta, -delta);
-                        // Intersect with display content rect. If we have system decor (status bar/
-                        // navigation bar), we want to exclude that from the tap detection.
-                        // Otherwise, if the app is partially placed under some system button (eg.
-                        // Recents, Home), pressing that button would cause a full series of
-                        // unwanted transfer focus/resume/pause, before we could go home.
-                        mTmpRect.intersect(mContentRect);
-                    }
-                    mTouchExcludeRegion.op(mTmpRect, Region.Op.DIFFERENCE);
-                }
-            }
+            final TaskStack stack = mStacks.get(stackNdx);
+            stack.setTouchExcludeRegion(
+                    focusedTask, delta, mTouchExcludeRegion, mContentRect, mTmpRect2);
         }
         // If we removed the focused task above, add it back and only leave its
         // outside touch area in the exclusion. TapDectector is not interested in
         // any touch inside the focused task itself.
-        if (addBackFocusedTask) {
+        if (!mTmpRect2.isEmpty()) {
             mTouchExcludeRegion.op(mTmpRect2, Region.Op.UNION);
         }
         final WindowState inputMethod = mService.mInputMethodWindow;
@@ -574,32 +523,18 @@
         return false;
     }
 
-    void onCompleteDeferredRemoval() {
-        boolean animating = false;
+    /** Returns true if a removal action is still being deferred. */
+    boolean checkCompleteDeferredRemoval() {
+        boolean stillDeferringRemoval = false;
         for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
             final TaskStack stack = mStacks.get(stackNdx);
-            if (stack.isAnimating()) {
-                animating = true;
-            } else {
-                if (stack.mDeferDetach) {
-                    mService.detachStackLocked(this, stack);
-                }
-                final ArrayList<Task> tasks = stack.getTasks();
-                for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
-                    final Task task = tasks.get(taskNdx);
-                    AppTokenList tokens = task.mAppTokens;
-                    for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) {
-                        AppWindowToken wtoken = tokens.get(tokenNdx);
-                        if (wtoken.mIsExiting) {
-                            wtoken.removeIfPossible();
-                        }
-                    }
-                }
-            }
+            stillDeferringRemoval |= stack.checkCompleteDeferredRemoval();
         }
-        if (!animating && mDeferredRemoval) {
+        if (!stillDeferringRemoval && mDeferredRemoval) {
             mService.onDisplayRemoved(mDisplayId);
+            return false;
         }
+        return true;
     }
 
     void rotateBounds(int oldRotation, int newRotation, Rect bounds) {
@@ -870,17 +805,17 @@
         final WindowToken wToken = win.mToken;
 
         // Figure out where the window should go, based on the order of applications.
-        final GetWindowOnDisplaySearchResults result = new GetWindowOnDisplaySearchResults();
+        mTmpGetWindowOnDisplaySearchResult.reset();
         for (int i = mStacks.size() - 1; i >= 0; --i) {
             final TaskStack stack = mStacks.get(i);
-            stack.getWindowOnDisplayBeforeToken(this, wToken, result);
-            if (result.reachedToken) {
+            stack.getWindowOnDisplayBeforeToken(this, wToken, mTmpGetWindowOnDisplaySearchResult);
+            if (mTmpGetWindowOnDisplaySearchResult.reachedToken) {
                 // We have reach the token we are interested in. End search.
                 break;
             }
         }
 
-        WindowState pos = result.foundWindow;
+        WindowState pos = mTmpGetWindowOnDisplaySearchResult.foundWindow;
 
         // We now know the index into the apps. If we found an app window above, that gives us the
         // position; else we need to look some more.
@@ -902,17 +837,17 @@
         }
 
         // Continue looking down until we find the first token that has windows on this display.
-        result.reset();
+        mTmpGetWindowOnDisplaySearchResult.reset();
         for (int i = mStacks.size() - 1; i >= 0; --i) {
             final TaskStack stack = mStacks.get(i);
-            stack.getWindowOnDisplayAfterToken(this, wToken, result);
-            if (result.foundWindow != null) {
+            stack.getWindowOnDisplayAfterToken(this, wToken, mTmpGetWindowOnDisplaySearchResult);
+            if (mTmpGetWindowOnDisplaySearchResult.foundWindow != null) {
                 // We have found a window after the token. End search.
                 break;
             }
         }
 
-        pos = result.foundWindow;
+        pos = mTmpGetWindowOnDisplaySearchResult.foundWindow;
 
         if (pos != null) {
             // Move in front of any windows attached to this one.
@@ -1224,7 +1159,7 @@
         }
     }
 
-    static final class GetWindowOnDisplaySearchResults {
+    static final class GetWindowOnDisplaySearchResult {
         boolean reachedToken;
         WindowState foundWindow;
 
@@ -1233,4 +1168,14 @@
             foundWindow = null;
         }
     }
+
+    static final class TaskForResizePointSearchResult {
+        boolean searchDone;
+        Task taskForResize;
+
+        void reset() {
+            searchDone = false;
+            taskForResize = null;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 5c6cb6b..837f0d3 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -54,7 +54,7 @@
     static final int BOUNDS_CHANGE_SIZE = 1 << 1;
 
     TaskStack mStack;
-    final AppTokenList mAppTokens = new AppTokenList();
+    private final AppTokenList mAppTokens = new AppTokenList();
     final int mTaskId;
     final int mUserId;
     boolean mDeferRemoval = false;
@@ -182,6 +182,17 @@
         }
     }
 
+    boolean checkCompleteDeferredRemoval() {
+        boolean stillDeferringRemoval = false;
+
+        for (int tokenNdx = mAppTokens.size() - 1; tokenNdx >= 0; --tokenNdx) {
+            final AppWindowToken token = mAppTokens.get(tokenNdx);
+            stillDeferringRemoval |= token.checkCompleteDeferredRemoval();
+        }
+
+        return stillDeferringRemoval;
+    }
+
     // TODO: Don't forget to switch to WC.detachChild
     void detachChild(AppWindowToken wtoken) {
         if (!removeAppToken(wtoken)) {
@@ -684,6 +695,27 @@
         return false;
     }
 
+    void checkAppWindowsReadyToShow(int displayId) {
+        for (int i = mAppTokens.size() - 1; i >= 0; --i) {
+            final AppWindowToken aToken = mAppTokens.get(i);
+            aToken.checkAppWindowsReadyToShow(displayId);
+        }
+    }
+
+    void updateAllDrawn(int displayId) {
+        for (int i = mAppTokens.size() - 1; i >= 0; --i) {
+            final AppWindowToken aToken = mAppTokens.get(i);
+            aToken.updateAllDrawn(displayId);
+        }
+    }
+
+    void stepAppWindowsAnimation(long currentTime, int displayId) {
+        for (int i = mAppTokens.size() - 1; i >= 0; --i) {
+            final AppWindowToken aToken = mAppTokens.get(i);
+            aToken.stepAppWindowsAnimation(currentTime, displayId);
+        }
+    }
+
     void onAppTransitionDone() {
         for (int i = mAppTokens.size() - 1; i >= 0; --i) {
             final AppWindowToken token = mAppTokens.get(i);
@@ -706,7 +738,7 @@
     }
 
     void getWindowOnDisplayBeforeToken(DisplayContent dc, WindowToken token,
-            DisplayContent.GetWindowOnDisplaySearchResults result) {
+            DisplayContent.GetWindowOnDisplaySearchResult result) {
         for (int i = mAppTokens.size() - 1; i >= 0; --i) {
             final AppWindowToken current = mAppTokens.get(i);
             if (current == token) {
@@ -725,7 +757,7 @@
     }
 
     void getWindowOnDisplayAfterToken(DisplayContent dc, WindowToken token,
-            DisplayContent.GetWindowOnDisplaySearchResults result) {
+            DisplayContent.GetWindowOnDisplaySearchResult result) {
         for (int i = mAppTokens.size() - 1; i >= 0; --i) {
             final AppWindowToken current = mAppTokens.get(i);
             if (!result.reachedToken) {
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 25f3c0a..39ec032 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -32,6 +32,7 @@
 import static android.view.WindowManager.DOCKED_RIGHT;
 import static android.view.WindowManager.DOCKED_TOP;
 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
+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;
@@ -39,6 +40,7 @@
 import android.app.ActivityManager.StackId;
 import android.content.res.Configuration;
 import android.graphics.Rect;
+import android.graphics.Region;
 import android.os.Debug;
 import android.os.RemoteException;
 import android.util.EventLog;
@@ -48,6 +50,7 @@
 import android.view.Surface;
 import android.view.animation.Animation;
 
+import android.view.WindowManagerPolicy;
 import com.android.internal.policy.DividerSnapAlgorithm;
 import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
 import com.android.internal.policy.DockedDividerUtils;
@@ -113,6 +116,7 @@
     final AppTokenList mExitingAppTokens = new AppTokenList();
 
     /** Detach this stack from its display when animation completes. */
+    // TODO: maybe tie this to WindowContainer#detachChild some how...
     boolean mDeferDetach;
 
     private final Rect mTmpAdjustedBounds = new Rect();
@@ -147,10 +151,6 @@
         return mDisplayContent;
     }
 
-    ArrayList<Task> getTasks() {
-        return mTasks;
-    }
-
     Task findHomeTask() {
         if (mStackId != HOME_STACK_ID) {
             return null;
@@ -1232,6 +1232,122 @@
         return false;
     }
 
+    boolean hasTaskForUser(int userId) {
+        for (int i = mTasks.size() - 1; i >= 0; i--) {
+            final Task task = mTasks.get(i);
+            if (task.mUserId == userId) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    int taskIdFromPoint(int x, int y) {
+        getBounds(mTmpRect);
+        if (!mTmpRect.contains(x, y) || isAdjustedForMinimizedDockedStack()) {
+            return -1;
+        }
+
+        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
+            final Task task = mTasks.get(taskNdx);
+            final WindowState win = task.getTopVisibleAppMainWindow();
+            if (win == null) {
+                continue;
+            }
+            // We need to use the task's dim bounds (which is derived from the visible bounds of its
+            // apps windows) for any touch-related tests. Can't use the task's original bounds
+            // because it might be adjusted to fit the content frame. For example, the presence of
+            // the IME adjusting the windows frames when the app window is the IME target.
+            task.getDimBounds(mTmpRect);
+            if (mTmpRect.contains(x, y)) {
+                return task.mTaskId;
+            }
+        }
+
+        return -1;
+    }
+
+    void findTaskForResizePoint(int x, int y, int delta,
+            DisplayContent.TaskForResizePointSearchResult results) {
+        if (!StackId.isTaskResizeAllowed(mStackId)) {
+            results.searchDone = true;
+            return;
+        }
+
+        for (int i = mTasks.size() - 1; i >= 0; --i) {
+            final Task task = mTasks.get(i);
+            if (task.isFullscreen()) {
+                results.searchDone = true;
+                return;
+            }
+
+            // We need to use the task's dim bounds (which is derived from the visible bounds of
+            // its apps windows) for any touch-related tests. Can't use the task's original
+            // bounds because it might be adjusted to fit the content frame. One example is when
+            // the task is put to top-left quadrant, the actual visible area would not start at
+            // (0,0) after it's adjusted for the status bar.
+            task.getDimBounds(mTmpRect);
+            mTmpRect.inset(-delta, -delta);
+            if (mTmpRect.contains(x, y)) {
+                mTmpRect.inset(delta, delta);
+
+                results.searchDone = true;
+
+                if (!mTmpRect.contains(x, y)) {
+                    results.taskForResize = task;
+                    return;
+                }
+                // User touched inside the task. No need to look further,
+                // focus transfer will be handled in ACTION_UP.
+                return;
+            }
+        }
+    }
+
+    void setTouchExcludeRegion(Task focusedTask, int delta, Region touchExcludeRegion,
+            Rect contentRect, Rect postExclude) {
+        for (int i = mTasks.size() - 1; i >= 0; --i) {
+            final Task task = mTasks.get(i);
+            AppWindowToken token = task.getTopVisibleAppToken();
+            if (token == null || !token.hasContentToDisplay()) {
+                continue;
+            }
+
+            /**
+             * Exclusion region is the region that TapDetector doesn't care about.
+             * Here we want to remove all non-focused tasks from the exclusion region.
+             * We also remove the outside touch area for resizing for all freeform
+             * tasks (including the focused).
+             *
+             * We save the focused task region once we find it, and add it back at the end.
+             */
+
+            task.getDimBounds(mTmpRect);
+
+            if (task == focusedTask) {
+                // Add the focused task rect back into the exclude region once we are done
+                // processing stacks.
+                postExclude.set(mTmpRect);
+            }
+
+            final boolean isFreeformed = task.inFreeformWorkspace();
+            if (task != focusedTask || isFreeformed) {
+                if (isFreeformed) {
+                    // If the task is freeformed, enlarge the area to account for outside
+                    // touch area for resize.
+                    mTmpRect.inset(-delta, -delta);
+                    // Intersect with display content rect. If we have system decor (status bar/
+                    // navigation bar), we want to exclude that from the tap detection.
+                    // Otherwise, if the app is partially placed under some system button (eg.
+                    // Recents, Home), pressing that button would cause a full series of
+                    // unwanted transfer focus/resume/pause, before we could go home.
+                    mTmpRect.intersect(contentRect);
+                }
+                touchExcludeRegion.op(mTmpRect, Region.Op.DIFFERENCE);
+            }
+        }
+    }
+
     @Override  // AnimatesBounds
     public boolean setSize(Rect bounds) {
         synchronized (mService.mWindowMap) {
@@ -1322,6 +1438,63 @@
         }
     }
 
+    /** Returns true if a removal action is still being deferred. */
+    boolean checkCompleteDeferredRemoval() {
+        if (isAnimating()) {
+            return true;
+        }
+        if (mDeferDetach) {
+            mDisplayContent.detachChild(this);
+        }
+
+        boolean stillDeferringRemoval = false;
+        for (int i = mTasks.size() - 1; i >= 0; --i) {
+            final Task task = mTasks.get(i);
+            stillDeferringRemoval |= task.checkCompleteDeferredRemoval();
+        }
+        return stillDeferringRemoval;
+    }
+
+    void checkAppWindowsReadyToShow(int displayId) {
+        for (int i = mTasks.size() - 1; i >= 0; --i) {
+            final Task task = mTasks.get(i);
+            task.checkAppWindowsReadyToShow(displayId);
+        }
+    }
+
+    void updateAllDrawn(int displayId) {
+        for (int i = mTasks.size() - 1; i >= 0; --i) {
+            final Task task = mTasks.get(i);
+            task.updateAllDrawn(displayId);
+        }
+    }
+
+    void stepAppWindowsAnimation(long currentTime, int displayId) {
+        for (int i = mTasks.size() - 1; i >= 0; --i) {
+            final Task task = mTasks.get(i);
+            task.stepAppWindowsAnimation(currentTime, displayId);
+        }
+
+        // TODO: Why aren't we just using the loop above for this? mAppAnimator.animating isn't set
+        // below but is set in the loop above. See if it really matters...
+        final int exitingCount = mExitingAppTokens.size();
+        for (int i = 0; i < exitingCount; i++) {
+            final AppWindowAnimator appAnimator = mExitingAppTokens.get(i).mAppAnimator;
+            appAnimator.wasAnimating = appAnimator.animating;
+            if (appAnimator.stepAnimationLocked(currentTime, displayId)) {
+                mService.mAnimator.setAnimating(true);
+                mService.mAnimator.mAppWindowAnimating = true;
+            } else if (appAnimator.wasAnimating) {
+                // stopped animating, do one more pass through the layout
+                appAnimator.mAppToken.setAppLayoutChanges(
+                        WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER,
+                        "exiting appToken " + appAnimator.mAppToken + " done", displayId);
+                if (DEBUG_ANIM) Slog.v(TAG_WM,
+                        "updateWindowsApps...: done animating exiting " + appAnimator.mAppToken);
+            }
+        }
+    }
+
     void onAppTransitionDone() {
         for (int i = mTasks.size() - 1; i >= 0; --i) {
             final Task task = mTasks.get(i);
@@ -1350,7 +1523,7 @@
     }
 
     void getWindowOnDisplayBeforeToken(DisplayContent dc, WindowToken token,
-            DisplayContent.GetWindowOnDisplaySearchResults result) {
+            DisplayContent.GetWindowOnDisplaySearchResult result) {
         for (int i = mTasks.size() - 1; i >= 0; --i) {
             final Task task = mTasks.get(i);
             task.getWindowOnDisplayBeforeToken(dc, token, result);
@@ -1362,7 +1535,7 @@
     }
 
     void getWindowOnDisplayAfterToken(DisplayContent dc, WindowToken token,
-            DisplayContent.GetWindowOnDisplaySearchResults result) {
+            DisplayContent.GetWindowOnDisplaySearchResult result) {
         for (int i = mTasks.size() - 1; i >= 0; --i) {
             final Task task = mTasks.get(i);
             task.getWindowOnDisplayAfterToken(dc, token, result);
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 38f7e64..6665a93 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -29,7 +29,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_KEYGUARD;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
@@ -169,61 +168,6 @@
         mDisplayContentsAnimators.delete(displayId);
     }
 
-    private void updateAppWindowsLocked(int displayId) {
-        ArrayList<TaskStack> stacks = mService.getDisplayContentLocked(displayId).getStacks();
-        for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            final TaskStack stack = stacks.get(stackNdx);
-            final ArrayList<Task> tasks = stack.getTasks();
-            for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
-                final AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
-                for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) {
-                    final AppWindowAnimator appAnimator = tokens.get(tokenNdx).mAppAnimator;
-                    appAnimator.wasAnimating = appAnimator.animating;
-                    if (appAnimator.stepAnimationLocked(mCurrentTime, displayId)) {
-                        appAnimator.animating = true;
-                        setAnimating(true);
-                        mAppWindowAnimating = true;
-                    } else if (appAnimator.wasAnimating) {
-                        // stopped animating, do one more pass through the layout
-                        setAppLayoutChanges(appAnimator,
-                                WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER,
-                                "appToken " + appAnimator.mAppToken + " done", displayId);
-                        if (DEBUG_ANIM) Slog.v(TAG,
-                                "updateWindowsApps...: done animating " + appAnimator.mAppToken);
-                    }
-                }
-            }
-
-            mTmpExitingAppTokens.clear();
-            mTmpExitingAppTokens.addAll(stack.mExitingAppTokens);
-
-            final int exitingCount = mTmpExitingAppTokens.size();
-            for (int i = 0; i < exitingCount; i++) {
-                final AppWindowAnimator appAnimator = mTmpExitingAppTokens.get(i).mAppAnimator;
-                // stepAnimation can trigger finishExit->removeWindowInnerLocked
-                // ->performSurfacePlacement
-                // performSurfacePlacement will directly manipulate the mExitingAppTokens list
-                // so we need to iterate over a copy and check for modifications.
-                if (!stack.mExitingAppTokens.contains(appAnimator)) {
-                    continue;
-                }
-                appAnimator.wasAnimating = appAnimator.animating;
-                if (appAnimator.stepAnimationLocked(mCurrentTime, displayId)) {
-                    setAnimating(true);
-                    mAppWindowAnimating = true;
-                } else if (appAnimator.wasAnimating) {
-                    // stopped animating, do one more pass through the layout
-                    setAppLayoutChanges(appAnimator,
-                            WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER,
-                            "exiting appToken " + appAnimator.mAppToken + " done", displayId);
-                    if (DEBUG_ANIM) Slog.v(TAG,
-                            "updateWindowsApps...: done animating exiting "
-                                    + appAnimator.mAppToken);
-                }
-            }
-        }
-    }
-
     /**
      * @return The window that is currently hiding the Keyguard, or if it was hiding the Keyguard,
      *         and it's still animating.
@@ -648,54 +592,6 @@
         }
     }
 
-    /** See if any windows have been drawn, so they (and others associated with them) can now be
-     *  shown. */
-    private void testTokenMayBeDrawnLocked(int displayId) {
-        // See if any windows have been drawn, so they (and others
-        // associated with them) can now be shown.
-        final ArrayList<Task> tasks = mService.getDisplayContentLocked(displayId).getTasks();
-        final int numTasks = tasks.size();
-        for (int taskNdx = 0; taskNdx < numTasks; ++taskNdx) {
-            final AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
-            final int numTokens = tokens.size();
-            for (int tokenNdx = 0; tokenNdx < numTokens; ++tokenNdx) {
-                final AppWindowToken wtoken = tokens.get(tokenNdx);
-                AppWindowAnimator appAnimator = wtoken.mAppAnimator;
-                final boolean allDrawn = wtoken.allDrawn;
-                if (allDrawn != appAnimator.allDrawn) {
-                    appAnimator.allDrawn = allDrawn;
-                    if (allDrawn) {
-                        // The token has now changed state to having all
-                        // windows shown...  what to do, what to do?
-                        if (appAnimator.freezingScreen) {
-                            appAnimator.showAllWindowsLocked();
-                            wtoken.stopFreezingScreen(false, true);
-                            if (DEBUG_ORIENTATION) Slog.i(TAG,
-                                    "Setting mOrientationChangeComplete=true because wtoken "
-                                    + wtoken + " numInteresting=" + wtoken.numInterestingWindows
-                                    + " numDrawn=" + wtoken.numDrawnWindows);
-                            // This will set mOrientationChangeComplete and cause a pass through
-                            // layout.
-                            setAppLayoutChanges(appAnimator,
-                                    WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER,
-                                    "testTokenMayBeDrawnLocked: freezingScreen", displayId);
-                        } else {
-                            setAppLayoutChanges(appAnimator,
-                                    WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM,
-                                    "testTokenMayBeDrawnLocked", displayId);
-
-                            // We can now show all of the drawn windows!
-                            if (!mService.mOpeningApps.contains(wtoken)) {
-                                orAnimating(appAnimator.showAllWindowsLocked());
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-
     /** Locked on mService.mWindowMap. */
     private void animateLocked(long frameTimeNs) {
         if (!mInitialized) {
@@ -719,7 +615,8 @@
             final int numDisplays = mDisplayContentsAnimators.size();
             for (int i = 0; i < numDisplays; i++) {
                 final int displayId = mDisplayContentsAnimators.keyAt(i);
-                updateAppWindowsLocked(displayId);
+                final DisplayContent displayContent = mService.getDisplayContentLocked(displayId);
+                displayContent.stepAppWindowsAnimation(mCurrentTime);
                 DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.valueAt(i);
 
                 final ScreenRotationAnimation screenRotationAnimation =
@@ -757,8 +654,9 @@
 
             for (int i = 0; i < numDisplays; i++) {
                 final int displayId = mDisplayContentsAnimators.keyAt(i);
+                final DisplayContent displayContent = mService.getDisplayContentLocked(displayId);
 
-                testTokenMayBeDrawnLocked(displayId);
+                displayContent.checkAppWindowsReadyToShow();
 
                 final ScreenRotationAnimation screenRotationAnimation =
                         mDisplayContentsAnimators.valueAt(i).mScreenRotationAnimation;
@@ -766,12 +664,10 @@
                     screenRotationAnimation.updateSurfacesInTransaction();
                 }
 
-                orAnimating(mService.getDisplayContentLocked(displayId).animateDimLayers());
-                orAnimating(mService.getDisplayContentLocked(displayId).getDockedDividerController()
-                        .animate(mCurrentTime));
+                orAnimating(displayContent.animateDimLayers());
+                orAnimating(displayContent.getDockedDividerController().animate(mCurrentTime));
                 //TODO (multidisplay): Magnification is supported only for the default display.
-                if (mService.mAccessibilityController != null
-                        && displayId == Display.DEFAULT_DISPLAY) {
+                if (mService.mAccessibilityController != null && displayContent.isDefaultDisplay) {
                     mService.mAccessibilityController.drawMagnifiedRegionBorderIfNeededLocked();
                 }
             }
@@ -950,11 +846,6 @@
         }
     }
 
-    void setAppLayoutChanges(
-            AppWindowAnimator appAnimator, int changes, String reason, int displayId) {
-        appAnimator.mAppToken.setAppLayoutChanges(changes, reason, displayId);
-    }
-
     private DisplayContentsAnimator getDisplayContentsAnimatorLocked(int displayId) {
         DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.get(displayId);
         if (displayAnimator == null) {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index eaf6aaa..7273a06 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -251,6 +251,51 @@
         return mChildren.isEmpty() ? this : mChildren.peekLast();
     }
 
+    /** Returns true if there is still a removal being deferred */
+    boolean checkCompleteDeferredRemoval() {
+        boolean stillDeferringRemoval = false;
+
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = mChildren.get(i);
+            stillDeferringRemoval |= wc.checkCompleteDeferredRemoval();
+        }
+
+        return stillDeferringRemoval;
+    }
+
+    /** Checks if all windows in an app are all drawn and shows them if needed. */
+    // TODO: The displayId shouldn't be needed as there shouldn't be a container on more than one
+    // display. Remove once we migrate DisplayContent to use WindowContainer.
+    void checkAppWindowsReadyToShow(int displayId) {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = mChildren.get(i);
+            wc.checkAppWindowsReadyToShow(displayId);
+        }
+    }
+
+    /**
+     * Updates the current all drawn status for this container. That is all its children
+     * that should draw something have done so.
+     */
+    // TODO: The displayId shouldn't be needed as there shouldn't be a container on more than one
+    // display. Remove once we migrate DisplayContent to use WindowContainer.
+    void updateAllDrawn(int displayId) {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = mChildren.get(i);
+            wc.updateAllDrawn(displayId);
+        }
+    }
+
+    /** Step currently ongoing animation for App window containers. */
+    // TODO: The displayId shouldn't be needed as there shouldn't be a container on more than one
+    // display. Remove once we migrate DisplayContent to use WindowContainer.
+    void stepAppWindowsAnimation(long currentTime, int displayId) {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = mChildren.get(i);
+            wc.stepAppWindowsAnimation(currentTime, displayId);
+        }
+    }
+
     void onAppTransitionDone() {
         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 39544c0..507db7c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -162,7 +162,6 @@
 import java.net.Socket;
 import java.text.DateFormat;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -170,7 +169,6 @@
 
 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 import static android.app.StatusBarManager.DISABLE_MASK;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
@@ -216,7 +214,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_ORIENTATION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
@@ -927,6 +924,12 @@
         }
     };
 
+    final ArrayList<AppFreezeListener> mAppFreezeListeners = new ArrayList<>();
+
+    interface AppFreezeListener {
+        void onAppFreezeTimeout();
+    }
+
     public static WindowManagerService main(final Context context,
             final InputManagerService im,
             final boolean haveInputMethods, final boolean showBootMsgs,
@@ -3967,20 +3970,9 @@
         return null;
     }
 
-    void detachStackLocked(DisplayContent displayContent, TaskStack stack) {
-        displayContent.detachStack(stack);
-        if (stack.detachFromDisplay()) {
-            mWindowPlacerLocked.requestTraversal();
-        }
-        if (stack.mStackId == DOCKED_STACK_ID) {
-            getDefaultDisplayContentLocked().mDividerControllerLocked
-                    .notifyDockedStackExistsChanged(false);
-        }
-    }
-
     public void detachStack(int stackId) {
         synchronized (mWindowMap) {
-            TaskStack stack = mStackIdToStack.get(stackId);
+            final TaskStack stack = mStackIdToStack.get(stackId);
             if (stack != null) {
                 final DisplayContent displayContent = stack.getDisplayContent();
                 if (displayContent != null) {
@@ -3988,7 +3980,7 @@
                         stack.mDeferDetach = true;
                         return;
                     }
-                    detachStackLocked(displayContent, stack);
+                    displayContent.detachChild(stack);
                 }
             }
         }
@@ -4675,22 +4667,13 @@
         }
     }
 
-    /**
-     * Returns whether there is a docked task for the current user.
-     */
+    /** 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;
         }
-
-        final ArrayList<Task> tasks = stack.getTasks();
-        boolean hasUserTask = false;
-        for (int i = tasks.size() - 1; i >= 0 && !hasUserTask; i--) {
-            final Task task = tasks.get(i);
-            hasUserTask = (task.mUserId == userId);
-        }
-        return hasUserTask;
+        return stack.hasTaskForUser(userId);
     }
 
     /* Called by WindowState */
@@ -7382,20 +7365,8 @@
                     synchronized (mWindowMap) {
                         Slog.w(TAG_WM, "App freeze timeout expired.");
                         mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_TIMEOUT;
-                        final int numStacks = mStackIdToStack.size();
-                        for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
-                            final TaskStack stack = mStackIdToStack.valueAt(stackNdx);
-                            final ArrayList<Task> tasks = stack.getTasks();
-                            for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
-                                AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
-                                for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) {
-                                    AppWindowToken tok = tokens.get(tokenNdx);
-                                    if (tok.mAppAnimator.freezingScreen) {
-                                        Slog.w(TAG_WM, "Force clearing freeze: " + tok);
-                                        tok.stopFreezingScreen(true, true);
-                                    }
-                                }
-                            }
+                        for (int i = mAppFreezeListeners.size() - 1; i >=0 ; --i) {
+                            mAppFreezeListeners.get(i).onAppFreezeTimeout();
                         }
                     }
                     break;
@@ -10326,4 +10297,15 @@
             }
         }
     }
+
+    void registerAppFreezeListener(AppFreezeListener listener) {
+        if (!mAppFreezeListeners.contains(listener)) {
+            mAppFreezeListeners.add(listener);
+        }
+    }
+
+    void unregisterAppFreezeListener(AppFreezeListener listener) {
+        mAppFreezeListeners.remove(listener);
+    }
+
 }
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index f128cf9..cd93a8c 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -37,7 +37,6 @@
 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.DO_TRAVERSAL;
-import static com.android.server.wm.WindowManagerService.H.NOTIFY_ACTIVITY_DRAWN;
 import static com.android.server.wm.WindowManagerService.H.NOTIFY_APP_TRANSITION_STARTING;
 import static com.android.server.wm.WindowManagerService.H.NOTIFY_STARTING_WINDOW_DRAWN;
 import static com.android.server.wm.WindowManagerService.H.REPORT_LOSING_FOCUS;
@@ -560,7 +559,7 @@
 
         // Remove all deferred displays stacks, tasks, and activities.
         for (int displayNdx = mService.mDisplayContents.size() - 1; displayNdx >= 0; --displayNdx) {
-            mService.mDisplayContents.valueAt(displayNdx).onCompleteDeferredRemoval();
+            mService.mDisplayContents.valueAt(displayNdx).checkCompleteDeferredRemoval();
         }
 
         if (updateInputWindowsNeeded) {
@@ -865,7 +864,9 @@
             displayContent.stopDimmingIfNeeded();
 
             if (updateAllDrawn) {
-                updateAllDrawnLocked(displayContent);
+                // See if any windows have been drawn, so they (and others associated with them)
+                // can now be shown.
+                displayContent.updateAllDrawn();
             }
         }
 
@@ -1516,52 +1517,6 @@
         }
     }
 
-    private void updateAllDrawnLocked(DisplayContent displayContent) {
-        // See if any windows have been drawn, so they (and others
-        // associated with them) can now be shown.
-        ArrayList<TaskStack> stacks = displayContent.getStacks();
-        for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            final ArrayList<Task> tasks = stacks.get(stackNdx).getTasks();
-            for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
-                final AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
-                for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) {
-                    final AppWindowToken wtoken = tokens.get(tokenNdx);
-                    if (!wtoken.allDrawn) {
-                        int numInteresting = wtoken.numInterestingWindows;
-                        if (numInteresting > 0 && wtoken.numDrawnWindows >= numInteresting) {
-                            if (DEBUG_VISIBILITY)
-                                Slog.v(TAG, "allDrawn: " + wtoken
-                                    + " interesting=" + numInteresting
-                                    + " drawn=" + wtoken.numDrawnWindows);
-                            wtoken.allDrawn = true;
-                            // Force an additional layout pass where WindowStateAnimator#
-                            // commitFinishDrawingLocked() will call performShowLocked().
-                            displayContent.layoutNeeded = true;
-                            mService.mH.obtainMessage(NOTIFY_ACTIVITY_DRAWN,
-                                    wtoken.token).sendToTarget();
-                        }
-                    }
-                    if (!wtoken.allDrawnExcludingSaved) {
-                        int numInteresting = wtoken.numInterestingWindowsExcludingSaved;
-                        if (numInteresting > 0
-                                && wtoken.numDrawnWindowsExcludingSaved >= numInteresting) {
-                            if (DEBUG_VISIBILITY)
-                                Slog.v(TAG, "allDrawnExcludingSaved: " + wtoken
-                                    + " interesting=" + numInteresting
-                                    + " drawn=" + wtoken.numDrawnWindowsExcludingSaved);
-                            wtoken.allDrawnExcludingSaved = true;
-                            displayContent.layoutNeeded = true;
-                            if (wtoken.isAnimatingInvisibleWithSavedSurface()
-                                    && !mService.mFinishedEarlyAnim.contains(wtoken)) {
-                                mService.mFinishedEarlyAnim.add(wtoken);
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-
     private static int toBrightnessOverride(float value) {
         return (int)(value * PowerManager.BRIGHTNESS_ON);
     }
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index d1d612b..2f9ed50 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -29,7 +29,6 @@
 
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_SCRIM;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;