Made AppWindowToken.allAppWindows private

Pre-clean-up before switching class to using WindowContainer.

Bug: 30060889
Change-Id: Ic3d47d47b922668eeb70988ce883267b46ca9d72
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index 39a549d..950fb20 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -157,11 +157,9 @@
         }
 
         // Since we are finally starting our animation, we don't need the logic anymore to prevent
-        // the app from showing again if we just moved between stacks. See
-        // {@link WindowState#notifyMovedInStack}.
-        for (int i = mAppToken.allAppWindows.size() - 1; i >= 0; i--) {
-            mAppToken.allAppWindows.get(i).resetJustMovedInStack();
-        }
+        // the app from showing again if we just moved between stacks.
+        // See {@link WindowState#notifyMovedInStack}.
+        mAppToken.resetJustMovedInStack();
     }
 
     public void setDummyAnimation() {
@@ -234,23 +232,7 @@
     }
 
     void updateLayers() {
-        final int windowCount = mAppToken.allAppWindows.size();
-        final int adj = animLayerAdjustment;
-        thumbnailLayer = -1;
-        final WallpaperController wallpaperController = mService.mWallpaperControllerLocked;
-        for (int i = 0; i < windowCount; i++) {
-            final WindowState w = mAppToken.allAppWindows.get(i);
-            final WindowStateAnimator winAnimator = w.mWinAnimator;
-            winAnimator.mAnimLayer = w.mLayer + adj;
-            if (winAnimator.mAnimLayer > thumbnailLayer) {
-                thumbnailLayer = winAnimator.mAnimLayer;
-            }
-            if (DEBUG_LAYERS) Slog.v(TAG, "Updating layer " + w + ": " + winAnimator.mAnimLayer);
-            if (w == mService.mInputMethodTarget && !mService.mInputMethodTargetWaitingAnim) {
-                mService.mLayersController.setInputMethodAnimLayerAdjustment(adj);
-            }
-            wallpaperController.setAnimLayerAdjustment(w, adj);
-        }
+        thumbnailLayer = mAppToken.adjustAnimLayer(animLayerAdjustment);
     }
 
     private void stepThumbnailAnimation(long currentTime) {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 01be4c8..bbd6fd7 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -17,18 +17,27 @@
 package com.android.server.wm;
 
 import static android.app.ActivityManager.StackId;
-import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManagerPolicy.TRANSIT_ENTER;
+import static android.view.WindowManagerPolicy.TRANSIT_EXIT;
 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;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
+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_RESIZE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.H.NOTIFY_ACTIVITY_DRAWN;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
+import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_TIMEOUT;
+import static com.android.server.wm.WindowManagerService.logWithStack;
 
 import com.android.server.input.InputApplicationHandle;
 import com.android.server.wm.WindowManagerService.H;
@@ -37,8 +46,11 @@
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
+import android.os.Binder;
+import android.os.IBinder;
 import android.os.Message;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.util.Slog;
 import android.view.IApplicationToken;
 import android.view.View;
@@ -63,7 +75,7 @@
 
     // All of the windows and child windows that are included in this
     // application token.  Note this list is NOT sorted!
-    final WindowList allAppWindows = new WindowList();
+    private final WindowList allAppWindows = new WindowList();
     @NonNull final AppWindowAnimator mAppAnimator;
 
     final boolean voiceInteraction;
@@ -280,6 +292,149 @@
         }
     }
 
+    boolean setVisibility(WindowManager.LayoutParams lp,
+            boolean visible, int transit, boolean performLayout, boolean isVoiceInteraction) {
+
+        boolean delayed = false;
+        inPendingTransaction = false;
+
+        if (clientHidden == visible) {
+            clientHidden = !visible;
+            sendAppVisibilityToClients();
+        }
+
+        // Allow for state changes and animation to be applied if:
+        // * token is transitioning visibility state
+        // * or the token was marked as hidden and is exiting before we had a chance to play the
+        // transition animation
+        // * or this is an opening app and windows are being replaced.
+        boolean visibilityChanged = false;
+        if (hidden == visible || (hidden && mIsExiting) || (visible && waitingForReplacement())) {
+            final AccessibilityController accessibilityController = service.mAccessibilityController;
+            boolean changed = false;
+            if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM,
+                    "Changing app " + this + " hidden=" + hidden + " performLayout=" + performLayout);
+
+            boolean runningAppAnimation = false;
+
+            if (transit != AppTransition.TRANSIT_UNSET) {
+                if (mAppAnimator.animation == AppWindowAnimator.sDummyAnimation) {
+                    mAppAnimator.setNullAnimation();
+                }
+                if (service.applyAnimationLocked(this, lp, transit, visible, isVoiceInteraction)) {
+                    delayed = runningAppAnimation = true;
+                }
+                final WindowState window = findMainWindow();
+                //TODO (multidisplay): Magnification is supported only for the default display.
+                if (window != null && accessibilityController != null
+                        && window.getDisplayId() == DEFAULT_DISPLAY) {
+                    accessibilityController.onAppWindowTransitionLocked(window, transit);
+                }
+                changed = true;
+            }
+
+            final int windowsCount = allAppWindows.size();
+            for (int i = 0; i < windowsCount; i++) {
+                final WindowState win = allAppWindows.get(i);
+                if (win == startingWindow) {
+                    // Starting window that's exiting will be removed when the animation finishes.
+                    // Mark all relevant flags for that onExitAnimationDone will proceed all the way
+                    // to actually remove it.
+                    if (!visible && win.isVisibleNow() && mAppAnimator.isAnimating()) {
+                        win.mAnimatingExit = true;
+                        win.mRemoveOnExit = true;
+                        win.mWindowRemovalAllowed = true;
+                    }
+                    continue;
+                }
+
+                //Slog.i(TAG_WM, "Window " + win + ": vis=" + win.isVisible());
+                //win.dump("  ");
+                if (visible) {
+                    if (!win.isVisibleNow()) {
+                        if (!runningAppAnimation) {
+                            win.mWinAnimator.applyAnimationLocked(TRANSIT_ENTER, true);
+                            //TODO (multidisplay): Magnification is supported only for the default
+                            if (accessibilityController != null
+                                    && win.getDisplayId() == DEFAULT_DISPLAY) {
+                                accessibilityController.onWindowTransitionLocked(win, TRANSIT_ENTER);
+                            }
+                        }
+                        changed = true;
+                        win.setDisplayLayoutNeeded();
+                    }
+                } else if (win.isVisibleNow()) {
+                    if (!runningAppAnimation) {
+                        win.mWinAnimator.applyAnimationLocked(TRANSIT_EXIT, false);
+                        //TODO (multidisplay): Magnification is supported only for the default
+                        if (accessibilityController != null
+                                && win.getDisplayId() == DEFAULT_DISPLAY) {
+                            accessibilityController.onWindowTransitionLocked(win,TRANSIT_EXIT);
+                        }
+                    }
+                    changed = true;
+                    win.setDisplayLayoutNeeded();
+                }
+            }
+
+            hidden = hiddenRequested = !visible;
+            visibilityChanged = true;
+            if (!visible) {
+                stopFreezingScreen(true, true);
+            } else {
+                // If we are being set visible, and the starting window is
+                // not yet displayed, then make sure it doesn't get displayed.
+                WindowState swin = startingWindow;
+                if (swin != null && !swin.isDrawnLw()) {
+                    swin.mPolicyVisibility = false;
+                    swin.mPolicyVisibilityAfterAnim = false;
+                }
+            }
+
+            if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "setVisibility: " + this
+                    + ": hidden=" + hidden + " hiddenRequested=" + hiddenRequested);
+
+            if (changed) {
+                service.mInputMonitor.setUpdateInputWindowsNeededLw();
+                if (performLayout) {
+                    service.updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
+                            false /*updateInputWindows*/);
+                    service.mWindowPlacerLocked.performSurfacePlacement();
+                }
+                service.mInputMonitor.updateInputWindowsLw(false /*force*/);
+            }
+        }
+
+        if (mAppAnimator.animation != null) {
+            delayed = true;
+        }
+
+        for (int i = allAppWindows.size() - 1; i >= 0 && !delayed; i--) {
+            if (allAppWindows.get(i).mWinAnimator.isWindowAnimationSet()) {
+                delayed = true;
+            }
+        }
+
+        if (visibilityChanged) {
+            if (visible && !delayed) {
+                // The token was made immediately visible, there will be no entrance animation.
+                // We need to inform the client the enter animation was finished.
+                mEnteringAnimation = true;
+                service.mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(token);
+            }
+
+            if (!service.mClosingApps.contains(this) && !service.mOpeningApps.contains(this)) {
+                // The token is not closing nor opening, so even if there is an animation set, that
+                // doesn't mean that it goes through the normal app transition cycle so we have
+                // to inform the docked controller about visibility change.
+                service.getDefaultDisplayContentLocked().getDockedDividerController()
+                        .notifyAppVisibilityChanged();
+            }
+        }
+
+        return delayed;
+    }
+
     WindowState findMainWindow() {
         WindowState candidate = null;
         int j = allAppWindows.size();
@@ -323,6 +478,16 @@
         return false;
     }
 
+    boolean isVisibleForUser() {
+        for (int j = allAppWindows.size() - 1; j >= 0; j--) {
+            final WindowState w = allAppWindows.get(j);
+            if (!w.isHiddenFromUserLocked()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     void removeAppFromTaskLocked() {
         mIsExiting = false;
         removeAllWindows();
@@ -585,6 +750,27 @@
         windows.clear();
     }
 
+    @Override
+    void removeWindow(WindowState win) {
+        super.removeWindow(win);
+
+        allAppWindows.remove(win);
+
+        if (startingWindow == win) {
+            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Notify removed startingWindow " + win);
+            service.scheduleRemoveStartingWindowLocked(this);
+        } else if (allAppWindows.size() == 0 && startingData != null) {
+            // If this is the last window and we had requested a starting transition window,
+            // well there is no point now.
+            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Nulling last startingWindow");
+            startingData = null;
+        } else if (allAppWindows.size() == 1 && startingView != null) {
+            // If this is the last window except for a starting transition window,
+            // we need to get rid of the starting transition.
+            service.scheduleRemoveStartingWindowLocked(this);
+        }
+    }
+
     void removeAllDeadWindows() {
         for (int winNdx = allAppWindows.size() - 1; winNdx >= 0;
             // removeWindowLocked at bottom of loop may remove multiple entries from
@@ -691,6 +877,10 @@
     }
 
     void addWindow(WindowState w) {
+        if (allAppWindows.contains(w)) {
+            return;
+        }
+
         for (int i = allAppWindows.size() - 1; i >= 0; i--) {
             WindowState candidate = allAppWindows.get(i);
             if (candidate.mWillReplaceWindow && candidate.mReplacingWindow == null &&
@@ -819,6 +1009,350 @@
         }
     }
 
+    void resetJustMovedInStack() {
+        for (int i = allAppWindows.size() - 1; i >= 0; i--) {
+            allAppWindows.get(i).resetJustMovedInStack();
+        }
+    }
+
+    @Override
+    int adjustAnimLayer(int adj) {
+        int highestAnimLayer = super.adjustAnimLayer(adj);
+
+        final int windowCount = allAppWindows.size();
+
+        for (int i = 0; i < windowCount; i++) {
+            final WindowState w = allAppWindows.get(i);
+            w.adjustAnimLayer(adj);
+
+            final int animLayer = w.mWinAnimator.mAnimLayer;
+            if (DEBUG_LAYERS) Slog.v(TAG, "Updating layer " + w + ": " + animLayer);
+            if (animLayer > highestAnimLayer) {
+                highestAnimLayer = animLayer;
+            }
+            if (w == service.mInputMethodTarget && !service.mInputMethodTargetWaitingAnim) {
+                service.mLayersController.setInputMethodAnimLayerAdjustment(adj);
+            }
+        }
+
+        return highestAnimLayer;
+    }
+
+    @Override
+    int getHighestAnimLayer() {
+        int layer = super.getHighestAnimLayer();
+        for (int j = 0; j < allAppWindows.size(); j++) {
+            final WindowState win = allAppWindows.get(j);
+            if (win.mWinAnimator.mAnimLayer > layer) {
+                layer = win.mWinAnimator.mAnimLayer;
+            }
+        }
+        return layer;
+    }
+
+    void setWaitingForDrawnIfResizingChanged() {
+        for (int i = allAppWindows.size() - 1; i >= 0; --i) {
+            final WindowState win = allAppWindows.get(i);
+            if (win.isDragResizeChanged()) {
+                service.mWaitingForDrawn.add(win);
+            }
+        }
+    }
+
+    void resizeWindows() {
+        final ArrayList<WindowState> resizingWindows = service.mResizingWindows;
+        // Some windows won't go through the resizing process, if they don't have a surface, so
+        // destroy all saved surfaces here.
+        destroySavedSurfaces();
+
+        for (int winNdx = allAppWindows.size() - 1; winNdx >= 0; --winNdx) {
+            final WindowState win = allAppWindows.get(winNdx);
+            if (win.mHasSurface && !resizingWindows.contains(win)) {
+                if (DEBUG_RESIZE) Slog.d(TAG, "resizeWindows: Resizing " + win);
+                resizingWindows.add(win);
+
+                // If we are not drag resizing, force recreating of a new surface so updating
+                // the content and positioning that surface will be in sync.
+                //
+                // As we use this flag as a hint to freeze surface boundary updates,
+                // we'd like to only apply this to TYPE_BASE_APPLICATION,
+                // windows of TYPE_APPLICATION like dialogs, could appear
+                // to not be drag resizing while they resize, but we'd
+                // still like to manipulate their frame to update crop, etc...
+                //
+                // Anyway we don't need to synchronize position and content updates for these
+                // windows since they aren't at the base layer and could be moved around anyway.
+                if (!win.computeDragResizing() && win.mAttrs.type == TYPE_BASE_APPLICATION &&
+                        !mTask.mStack.getBoundsAnimating() && !win.isGoneForLayoutLw() &&
+                        !mTask.inPinnedWorkspace()) {
+                    win.setResizedWhileNotDragResizing(true);
+                }
+            }
+            if (win.isGoneForLayoutLw()) {
+                win.mResizedWhileGone = true;
+            }
+        }
+    }
+
+    void moveWindows() {
+        for (int winNdx = allAppWindows.size() - 1; winNdx >= 0; --winNdx) {
+            final WindowState win = allAppWindows.get(winNdx);
+            if (DEBUG_RESIZE) Slog.d(TAG, "moveWindows: Moving " + win);
+            win.mMovedByResize = true;
+        }
+    }
+
+    void notifyMovedInStack() {
+        for (int winNdx = allAppWindows.size() - 1; winNdx >= 0; --winNdx) {
+            final WindowState win = allAppWindows.get(winNdx);
+            win.notifyMovedInStack();
+        }
+    }
+
+    void resetDragResizingChangeReported() {
+        for (int winNdx = allAppWindows.size() - 1; winNdx >= 0; --winNdx) {
+            final WindowState win = allAppWindows.get(winNdx);
+            win.resetDragResizingChangeReported();
+        }
+    }
+
+    void detachDisplay() {
+        boolean doAnotherLayoutPass = false;
+        for (int winNdx = allAppWindows.size() - 1; winNdx >= 0; --winNdx) {
+            // We are in the middle of changing the state of displays/stacks/tasks. We need
+            // to finish that, before we let layout interfere with it.
+            service.removeWindowLocked(allAppWindows.get(winNdx));
+            doAnotherLayoutPass = true;
+        }
+        if (doAnotherLayoutPass) {
+            service.mWindowPlacerLocked.requestTraversal();
+        }
+    }
+
+    void forceWindowsScaleableInTransaction(boolean force) {
+        for (int winNdx = allAppWindows.size() - 1; winNdx >= 0; --winNdx) {
+            final WindowStateAnimator winAnimator = allAppWindows.get(winNdx).mWinAnimator;
+            if (winAnimator == null || !winAnimator.hasSurface()) {
+                continue;
+            }
+            winAnimator.mSurfaceController.forceScaleableInTransaction(force);
+        }
+    }
+
+    boolean isAnimating() {
+        for (int winNdx = allAppWindows.size() - 1; winNdx >= 0; --winNdx) {
+            final WindowStateAnimator winAnimator = allAppWindows.get(winNdx).mWinAnimator;
+            if (winAnimator.isAnimationSet() || winAnimator.mWin.mAnimatingExit) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    void setAppLayoutChanges(int changes, String reason, int displayId) {
+        final WindowAnimator windowAnimator = mAppAnimator.mAnimator;
+        for (int i = allAppWindows.size() - 1; i >= 0; i--) {
+            if (displayId == allAppWindows.get(i).getDisplayId()) {
+                windowAnimator.setPendingLayoutChanges(displayId, changes);
+                if (DEBUG_LAYOUT_REPEATS) {
+                    service.mWindowPlacerLocked.debugLayoutRepeats(
+                            reason, windowAnimator.getPendingLayoutChanges(displayId));
+                }
+                break;
+            }
+        }
+    }
+
+    void removeReplacedWindowIfNeeded(WindowState replacement) {
+        for (int i = allAppWindows.size() - 1; i >= 0; i--) {
+            final WindowState win = allAppWindows.get(i);
+            if (win.mWillReplaceWindow && win.mReplacingWindow == replacement
+                    && replacement.hasDrawnLw()) {
+                replacement.mSkipEnterAnimationForSeamlessReplacement = false;
+                win.removeReplacedWindow();
+            }
+        }
+    }
+
+    void startFreezingScreen() {
+        if (DEBUG_ORIENTATION) logWithStack(TAG, "Set freezing of " + appToken + ": hidden="
+                + hidden + " freezing=" + mAppAnimator.freezingScreen);
+        if (!hiddenRequested) {
+            if (!mAppAnimator.freezingScreen) {
+                mAppAnimator.freezingScreen = true;
+                mAppAnimator.lastFreezeDuration = 0;
+                service.mAppsFreezingScreen++;
+                if (service.mAppsFreezingScreen == 1) {
+                    service.startFreezingDisplayLocked(false, 0, 0);
+                    service.mH.removeMessages(H.APP_FREEZE_TIMEOUT);
+                    service.mH.sendEmptyMessageDelayed(H.APP_FREEZE_TIMEOUT, 2000);
+                }
+            }
+            final int count = allAppWindows.size();
+            for (int i = 0; i < count; i++) {
+                final WindowState w = allAppWindows.get(i);
+                w.mAppFreezing = true;
+            }
+        }
+    }
+
+    void stopFreezingScreen(boolean unfreezeSurfaceNow, boolean force) {
+        if (!mAppAnimator.freezingScreen) {
+            return;
+        }
+        if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Clear freezing of " + this + " force=" + force);
+        final int count = allAppWindows.size();
+        boolean unfrozeWindows = false;
+        for (int i = 0; i < count; i++) {
+            final WindowState w = allAppWindows.get(i);
+            if (w.mAppFreezing) {
+                w.mAppFreezing = false;
+                if (w.mHasSurface && !w.mOrientationChanging
+                        && service.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) {
+                    if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "set mOrientationChanging of " + w);
+                    w.mOrientationChanging = true;
+                    service.mWindowPlacerLocked.mOrientationChangeComplete = false;
+                }
+                w.mLastFreezeDuration = 0;
+                unfrozeWindows = true;
+                w.setDisplayLayoutNeeded();
+            }
+        }
+        if (force || unfrozeWindows) {
+            if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "No longer freezing: " + this);
+            mAppAnimator.freezingScreen = false;
+            mAppAnimator.lastFreezeDuration =
+                    (int)(SystemClock.elapsedRealtime() - service.mDisplayFreezeTime);
+            service.mAppsFreezingScreen--;
+            service.mLastFinishedFreezeSource = this;
+        }
+        if (unfreezeSurfaceNow) {
+            if (unfrozeWindows) {
+                service.mWindowPlacerLocked.performSurfacePlacement();
+            }
+            service.stopFreezingDisplayLocked();
+        }
+    }
+
+    boolean transferStartingWindow(IBinder transferFrom) {
+        final AppWindowToken fromToken = service.findAppWindowToken(transferFrom);
+        if (fromToken == null) {
+            return false;
+        }
+
+        final WindowState tStartingWindow = fromToken.startingWindow;
+        if (tStartingWindow != null && fromToken.startingView != null) {
+            // In this case, the starting icon has already been displayed, so start
+            // letting windows get shown immediately without any more transitions.
+            service.mSkipAppTransitionAnimation = true;
+
+            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Moving existing starting " + tStartingWindow
+                    + " from " + fromToken + " to " + this);
+
+            final long origId = Binder.clearCallingIdentity();
+
+            // Transfer the starting window over to the new token.
+            startingData = fromToken.startingData;
+            startingView = fromToken.startingView;
+            startingDisplayed = fromToken.startingDisplayed;
+            fromToken.startingDisplayed = false;
+            startingWindow = tStartingWindow;
+            reportedVisible = fromToken.reportedVisible;
+            fromToken.startingData = null;
+            fromToken.startingView = null;
+            fromToken.startingWindow = null;
+            fromToken.startingMoved = true;
+            tStartingWindow.mToken = this;
+            tStartingWindow.mRootToken = this;
+            tStartingWindow.mAppToken = this;
+
+            if (DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE || DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
+                    "Removing starting window: " + tStartingWindow);
+            tStartingWindow.getWindowList().remove(tStartingWindow);
+            service.mWindowsChanged = true;
+            if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
+                    "Removing starting " + tStartingWindow + " from " + fromToken);
+            fromToken.removeWindow(tStartingWindow);
+            fromToken.allAppWindows.remove(tStartingWindow);
+            addWindowToList(tStartingWindow);
+
+            // Propagate other interesting state between the tokens. If the old token is displayed,
+            // we should immediately force the new one to be displayed. If it is animating, we need
+            // to move that animation to the new one.
+            if (fromToken.allDrawn) {
+                allDrawn = true;
+                deferClearAllDrawn = fromToken.deferClearAllDrawn;
+            }
+            if (fromToken.firstWindowDrawn) {
+                firstWindowDrawn = true;
+            }
+            if (!fromToken.hidden) {
+                hidden = false;
+                hiddenRequested = false;
+            }
+            if (clientHidden != fromToken.clientHidden) {
+                clientHidden = fromToken.clientHidden;
+                sendAppVisibilityToClients();
+            }
+            fromToken.mAppAnimator.transferCurrentAnimation(
+                    mAppAnimator, tStartingWindow.mWinAnimator);
+
+            service.updateFocusedWindowLocked(
+                    UPDATE_FOCUS_WILL_PLACE_SURFACES, true /*updateInputWindows*/);
+            service.getDefaultDisplayContentLocked().layoutNeeded = true;
+            service.mWindowPlacerLocked.performSurfacePlacement();
+            Binder.restoreCallingIdentity(origId);
+            return true;
+        } else if (fromToken.startingData != null) {
+            // The previous app was getting ready to show a
+            // starting window, but hasn't yet done so.  Steal it!
+            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
+                    "Moving pending starting from " + fromToken + " to " + this);
+            startingData = fromToken.startingData;
+            fromToken.startingData = null;
+            fromToken.startingMoved = true;
+            final Message m = service.mH.obtainMessage(H.ADD_STARTING, this);
+            // Note: we really want to do sendMessageAtFrontOfQueue() because we want to process the
+            // message ASAP, before any other queued messages.
+            service.mH.sendMessageAtFrontOfQueue(m);
+            return true;
+        }
+
+        final AppWindowAnimator tAppAnimator = fromToken.mAppAnimator;
+        final AppWindowAnimator wAppAnimator = mAppAnimator;
+        if (tAppAnimator.thumbnail != null) {
+            // The old token is animating with a thumbnail, transfer that to the new token.
+            if (wAppAnimator.thumbnail != null) {
+                wAppAnimator.thumbnail.destroy();
+            }
+            wAppAnimator.thumbnail = tAppAnimator.thumbnail;
+            wAppAnimator.thumbnailLayer = tAppAnimator.thumbnailLayer;
+            wAppAnimator.thumbnailAnimation = tAppAnimator.thumbnailAnimation;
+            tAppAnimator.thumbnail = null;
+        }
+        return false;
+    }
+
+    int getWindowsCount() {
+        return allAppWindows.size();
+    }
+
+    void setAllAppWinAnimators() {
+        final ArrayList<WindowStateAnimator> allAppWinAnimators = mAppAnimator.mAllAppWinAnimators;
+        allAppWinAnimators.clear();
+
+        final int windowsCount = allAppWindows.size();
+        for (int j = 0; j < windowsCount; j++) {
+            allAppWinAnimators.add(allAppWindows.get(j).mWinAnimator);
+        }
+    }
+
+    /** Returns true if the app token windows list is empty. */
+    @Override
+    boolean isEmpty() {
+        return allAppWindows.isEmpty();
+    }
+
     @Override
     void dump(PrintWriter pw, String prefix) {
         super.dump(pw, prefix);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 7394309..048d980 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -39,6 +39,7 @@
 import android.view.DisplayInfo;
 import android.view.Surface;
 
+import android.view.SurfaceControl;
 import com.android.server.EventLogTags;
 
 import java.io.PrintWriter;
@@ -178,11 +179,7 @@
         resizeLocked(bounds, config, false /* force */);
 
         for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) {
-            final ArrayList<WindowState> windows = mAppTokens.get(activityNdx).allAppWindows;
-            for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
-                final WindowState win = windows.get(winNdx);
-                win.notifyMovedInStack();
-            }
+            mAppTokens.get(activityNdx).notifyMovedInStack();
         }
     }
 
@@ -478,11 +475,7 @@
 
     void resetDragResizingChangeReported() {
         for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) {
-            final ArrayList<WindowState> windows = mAppTokens.get(activityNdx).allAppWindows;
-            for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
-                final WindowState win = windows.get(winNdx);
-                win.resetDragResizingChangeReported();
-            }
+            mAppTokens.get(activityNdx).resetDragResizingChangeReported();
         }
     }
 
@@ -498,15 +491,15 @@
      * Adds all of the tasks windows to {@link WindowManagerService#mWaitingForDrawn} if drag
      * resizing state of the window has been changed.
      */
-    void addWindowsWaitingForDrawnIfResizingChanged() {
-        for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) {
-            final ArrayList<WindowState> windows = mAppTokens.get(activityNdx).allAppWindows;
-            for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
-                final WindowState win = windows.get(winNdx);
-                if (win.isDragResizeChanged()) {
-                    mService.mWaitingForDrawn.add(win);
-                }
-            }
+    void setWaitingForDrawnIfResizingChanged() {
+        for (int i = mAppTokens.size() - 1; i >= 0; --i) {
+            mAppTokens.get(i).setWaitingForDrawnIfResizingChanged();
+        }
+    }
+
+    void detachDisplay() {
+        for (int i = mAppTokens.size() - 1; i >= 0; --i) {
+            mAppTokens.get(i).detachDisplay();
         }
     }
 
@@ -546,52 +539,14 @@
     }
 
     void resizeWindows() {
-        final ArrayList<WindowState> resizingWindows = mService.mResizingWindows;
         for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) {
-            final AppWindowToken atoken = mAppTokens.get(activityNdx);
-
-            // Some windows won't go through the resizing process, if they don't have a surface, so
-            // destroy all saved surfaces here.
-            atoken.destroySavedSurfaces();
-            final ArrayList<WindowState> windows = atoken.allAppWindows;
-            for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
-                final WindowState win = windows.get(winNdx);
-                if (win.mHasSurface && !resizingWindows.contains(win)) {
-                    if (DEBUG_RESIZE) Slog.d(TAG, "resizeWindows: Resizing " + win);
-                    resizingWindows.add(win);
-
-                    // If we are not drag resizing, force recreating of a new surface so updating
-                    // the content and positioning that surface will be in sync.
-                    //
-                    // As we use this flag as a hint to freeze surface boundary updates,
-                    // we'd like to only apply this to TYPE_BASE_APPLICATION,
-                    // windows of TYPE_APPLICATION like dialogs, could appear
-                    // to not be drag resizing while they resize, but we'd
-                    // still like to manipulate their frame to update crop, etc...
-                    //
-                    // Anyway we don't need to synchronize position and content updates for these
-                    // windows since they aren't at the base layer and could be moved around anyway.
-                    if (!win.computeDragResizing() && win.mAttrs.type == TYPE_BASE_APPLICATION &&
-                            !mStack.getBoundsAnimating() && !win.isGoneForLayoutLw() &&
-                            !inPinnedWorkspace()) {
-                        win.setResizedWhileNotDragResizing(true);
-                    }
-                }
-                if (win.isGoneForLayoutLw()) {
-                    win.mResizedWhileGone = true;
-                }
-            }
+            mAppTokens.get(activityNdx).resizeWindows();
         }
     }
 
     void moveWindows() {
-        for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) {
-            final ArrayList<WindowState> windows = mAppTokens.get(activityNdx).allAppWindows;
-            for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
-                final WindowState win = windows.get(winNdx);
-                if (DEBUG_RESIZE) Slog.d(TAG, "moveWindows: Moving " + win);
-                win.mMovedByResize = true;
-            }
+        for (int i = mAppTokens.size() - 1; i >= 0; --i) {
+            mAppTokens.get(i).moveWindows();
         }
     }
 
@@ -621,11 +576,8 @@
     boolean isVisibleForUser() {
         for (int i = mAppTokens.size() - 1; i >= 0; i--) {
             final AppWindowToken appToken = mAppTokens.get(i);
-            for (int j = appToken.allAppWindows.size() - 1; j >= 0; j--) {
-                WindowState window = appToken.allAppWindows.get(j);
-                if (!window.isHiddenFromUserLocked()) {
-                    return true;
-                }
+            if (appToken.isVisibleForUser()) {
+                return true;
             }
         }
         return false;
@@ -697,6 +649,27 @@
         return mStack.getDisplayContent().getDisplayInfo();
     }
 
+    void forceWindowsScaleable(boolean force) {
+        SurfaceControl.openTransaction();
+        try {
+            for (int i = mAppTokens.size() - 1; i >= 0; i--) {
+                mAppTokens.get(i).forceWindowsScaleableInTransaction(force);
+            }
+        } finally {
+            SurfaceControl.closeTransaction();
+        }
+    }
+
+    boolean isAnimating() {
+        for (int i = mAppTokens.size() - 1; i >= 0; i--) {
+            final AppWindowToken aToken = mAppTokens.get(i);
+            if (aToken.isAnimating()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     @Override
     public String toString() {
         return "{taskId=" + mTaskId + " appTokens=" + mAppTokens + " mdr=" + mDeferRemoval + "}";
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index b137840..d2ec792 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -477,15 +477,9 @@
 
     boolean isAnimating() {
         for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
-            final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens;
-            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
-                final ArrayList<WindowState> windows = activities.get(activityNdx).allAppWindows;
-                for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
-                    final WindowStateAnimator winAnimator = windows.get(winNdx).mWinAnimator;
-                    if (winAnimator.isAnimationSet() || winAnimator.mWin.mAnimatingExit) {
-                        return true;
-                    }
-                }
+            final Task task = mTasks.get(taskNdx);
+            if (task.isAnimating()) {
+                return true;
             }
         }
         return false;
@@ -532,12 +526,11 @@
         }
 
         if (StackId.windowsAreScaleable(mStackId)) {
-            // We force windows out of SCALING_MODE_FREEZE
-            // so that we can continue to animate them
+            // We force windows out of SCALING_MODE_FREEZE so that we can continue to animate them
             // while a resize is pending.
-            forceWindowsScaleable(task, true);
+            task.forceWindowsScaleable(true);
         } else {
-            forceWindowsScaleable(task, false);
+            task.forceWindowsScaleable(false);
         }
         EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, task.mTaskId, toTop ? 1 : 0, position);
     }
@@ -773,21 +766,8 @@
     void detachDisplay() {
         EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId);
 
-        boolean doAnotherLayoutPass = false;
         for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
-            final AppTokenList appWindowTokens = mTasks.get(taskNdx).mAppTokens;
-            for (int appNdx = appWindowTokens.size() - 1; appNdx >= 0; --appNdx) {
-                final WindowList appWindows = appWindowTokens.get(appNdx).allAppWindows;
-                for (int winNdx = appWindows.size() - 1; winNdx >= 0; --winNdx) {
-                    // We are in the middle of changing the state of displays/stacks/tasks. We need
-                    // to finish that, before we let layout interfere with it.
-                    mService.removeWindowLocked(appWindows.get(winNdx));
-                    doAnotherLayoutPass = true;
-                }
-            }
-        }
-        if (doAnotherLayoutPass) {
-            mService.mWindowPlacerLocked.requestTraversal();
+            mTasks.get(taskNdx).detachDisplay();
         }
 
         close();
@@ -922,7 +902,7 @@
             final Task task = mTasks.get(j);
             if (task.isVisibleForUser()) {
                 task.setDragResizing(true, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
-                task.addWindowsWaitingForDrawnIfResizingChanged();
+                task.setWaitingForDrawnIfResizingChanged();
             }
         }
     }
@@ -1290,25 +1270,6 @@
         return true;
     }
 
-    void forceWindowsScaleable(Task task, boolean force) {
-        SurfaceControl.openTransaction();
-        try {
-            final ArrayList<AppWindowToken> activities = task.mAppTokens;
-            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
-                final ArrayList<WindowState> windows = activities.get(activityNdx).allAppWindows;
-                for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
-                    final WindowStateAnimator winAnimator = windows.get(winNdx).mWinAnimator;
-                    if (winAnimator == null || !winAnimator.hasSurface()) {
-                        continue;
-                    }
-                    winAnimator.mSurfaceController.forceScaleableInTransaction(force);
-                }
-            }
-        } finally {
-            SurfaceControl.closeTransaction();
-        }
-    }
-
     @Override  // AnimatesBounds
     public void onAnimationStart() {
         synchronized (mService.mWindowMap) {
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index a7a7a39..6e7313c 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -624,7 +624,7 @@
                         // windows shown...  what to do, what to do?
                         if (appAnimator.freezingScreen) {
                             appAnimator.showAllWindowsLocked();
-                            mService.unsetAppFreezingScreenLocked(wtoken, false, true);
+                            wtoken.stopFreezingScreen(false, true);
                             if (DEBUG_ORIENTATION) Slog.i(TAG,
                                     "Setting mOrientationChangeComplete=true because wtoken "
                                     + wtoken + " numInteresting=" + wtoken.numInterestingWindows
@@ -800,21 +800,23 @@
     }
 
     private void removeReplacedWindowsLocked() {
-        if (SHOW_TRANSACTIONS) Slog.i(
-                TAG, ">>> OPEN TRANSACTION removeReplacedWindows");
+        if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION removeReplacedWindows");
         SurfaceControl.openTransaction();
         try {
             for (int i = mService.mDisplayContents.size() - 1; i >= 0; i--) {
                 DisplayContent display = mService.mDisplayContents.valueAt(i);
                 final WindowList windows = mService.getWindowListLocked(display.getDisplayId());
                 for (int j = windows.size() - 1; j >= 0; j--) {
-                    windows.get(j).maybeRemoveReplacedWindow();
+                    final WindowState win = windows.get(j);
+                    final AppWindowToken aToken = win.mAppToken;
+                    if (aToken != null) {
+                        aToken.removeReplacedWindowIfNeeded(win);
+                    }
                 }
             }
         } finally {
             SurfaceControl.closeTransaction();
-            if (SHOW_TRANSACTIONS) Slog.i(
-                    TAG, "<<< CLOSE TRANSACTION removeReplacedWindows");
+            if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION removeReplacedWindows");
         }
         mRemoveReplacedWindows = false;
     }
@@ -903,19 +905,9 @@
         }
     }
 
-    void setAppLayoutChanges(final AppWindowAnimator appAnimator, final int changes, String reason,
-            final int displayId) {
-        WindowList windows = appAnimator.mAppToken.allAppWindows;
-        for (int i = windows.size() - 1; i >= 0; i--) {
-            if (displayId == windows.get(i).getDisplayId()) {
-                setPendingLayoutChanges(displayId, changes);
-                if (DEBUG_LAYOUT_REPEATS) {
-                    mWindowPlacerLocked.debugLayoutRepeats(reason,
-                            getPendingLayoutChanges(displayId));
-                }
-                break;
-            }
-        }
+    void setAppLayoutChanges(
+            AppWindowAnimator appAnimator, int changes, String reason, int displayId) {
+        appAnimator.mAppToken.setAppLayoutChanges(changes, reason, displayId);
     }
 
     private DisplayContentsAnimator getDisplayContentsAnimatorLocked(int displayId) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index aceff33..6b46ddd 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -204,8 +204,7 @@
 import static android.view.WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY;
 import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
-import static android.view.WindowManagerPolicy.TRANSIT_EXIT;
-import static android.view.WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
+import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
 import static com.android.server.wm.AppWindowAnimator.PROLONG_ANIMATION_AT_END;
 import static com.android.server.wm.AppWindowAnimator.PROLONG_ANIMATION_AT_START;
 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
@@ -886,7 +885,7 @@
     private final DisplayContentList mReconfigureOnConfigurationChanged = new DisplayContentList();
 
     /** Listener to notify activity manager about app transitions. */
-    private final WindowManagerInternal.AppTransitionListener mActivityManagerAppTransitionNotifier
+    final WindowManagerInternal.AppTransitionListener mActivityManagerAppTransitionNotifier
             = new WindowManagerInternal.AppTransitionListener() {
 
         @Override
@@ -1872,19 +1871,6 @@
         }
     }
 
-    private void setupWindowForRemoveOnExit(WindowState win) {
-        win.mRemoveOnExit = true;
-        win.setDisplayLayoutNeeded();
-        // Request a focus update as this window's input channel is already gone. Otherwise
-        // we could have no focused window in input manager.
-        final boolean focusChanged = updateFocusedWindowLocked(
-                UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
-        mWindowPlacerLocked.performSurfacePlacement();
-        if (focusChanged) {
-            mInputMonitor.updateInputWindowsLw(false /*force*/);
-        }
-    }
-
     public void removeWindow(Session session, IWindow client) {
         synchronized(mWindowMap) {
             WindowState win = windowForClientLocked(session, client, false);
@@ -1896,145 +1882,7 @@
     }
 
     void removeWindowLocked(WindowState win) {
-        removeWindowLocked(win, false);
-    }
-
-    void removeWindowLocked(WindowState win, boolean keepVisibleDeadWindow) {
-        win.mWindowRemovalAllowed = true;
-        if (DEBUG_ADD_REMOVE) Slog.v(TAG,
-                "removeWindowLocked: " + win + " callers=" + Debug.getCallers(4));
-
-        final boolean startingWindow = win.mAttrs.type == TYPE_APPLICATION_STARTING;
-        if (startingWindow) {
-            if (DEBUG_STARTING_WINDOW) Slog.d(TAG_WM, "Starting window removed " + win);
-        }
-
-        if (localLOGV || DEBUG_FOCUS || DEBUG_FOCUS_LIGHT && win == mCurrentFocus) Slog.v(
-                TAG_WM, "Remove " + win + " client="
-                + Integer.toHexString(System.identityHashCode(win.mClient.asBinder()))
-                + ", surfaceController=" + win.mWinAnimator.mSurfaceController + " Callers="
-                + Debug.getCallers(4));
-
-        final long origId = Binder.clearCallingIdentity();
-
-        win.disposeInputChannel();
-
-        if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM,
-                "Remove " + win + ": mSurfaceController=" + win.mWinAnimator.mSurfaceController
-                + " mAnimatingExit=" + win.mAnimatingExit
-                + " mRemoveOnExit=" + win.mRemoveOnExit
-                + " mHasSurface=" + win.mHasSurface
-                + " surfaceShowing=" + win.mWinAnimator.getShown()
-                + " isAnimationSet=" + win.mWinAnimator.isAnimationSet()
-                + " app-animation="
-                + (win.mAppToken != null ? win.mAppToken.mAppAnimator.animation : null)
-                + " mWillReplaceWindow=" + win.mWillReplaceWindow
-                + " inPendingTransaction="
-                + (win.mAppToken != null ? win.mAppToken.inPendingTransaction : false)
-                + " mDisplayFrozen=" + mDisplayFrozen
-                + " callers=" + Debug.getCallers(6));
-        // Visibility of the removed window. Will be used later to update orientation later on.
-        boolean wasVisible = false;
-        // First, see if we need to run an animation. If we do, we have to hold off on removing the
-        // window until the animation is done. If the display is frozen, just remove immediately,
-        // since the animation wouldn't be seen.
-        if (win.mHasSurface && okToDisplay()) {
-            final AppWindowToken appToken = win.mAppToken;
-            if (win.mWillReplaceWindow) {
-                // This window is going to be replaced. We need to keep it around until the new one
-                // gets added, then we will get rid of this one.
-                if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "Preserving " + win + " until the new one is "
-                        + "added");
-                // TODO: We are overloading mAnimatingExit flag to prevent the window state from
-                // been removed. We probably need another flag to indicate that window removal
-                // should be deffered vs. overloading the flag that says we are playing an exit
-                // animation.
-                win.mAnimatingExit = true;
-                win.mReplacingRemoveRequested = true;
-                Binder.restoreCallingIdentity(origId);
-                return;
-            }
-
-            if (win.isAnimatingWithSavedSurface() && !appToken.allDrawnExcludingSaved) {
-                // We started enter animation early with a saved surface, now the app asks to remove
-                // this window. If we remove it now and the app is not yet drawn, we'll show a
-                // flicker. Delay the removal now until it's really drawn.
-                if (DEBUG_ADD_REMOVE) {
-                    Slog.d(TAG_WM, "removeWindowLocked: delay removal of " + win
-                            + " due to early animation");
-                }
-                // Do not set mAnimatingExit to true here, it will cause the surface to be hidden
-                // immediately after the enter animation is done. If the app is not yet drawn then
-                // it will show up as a flicker.
-                setupWindowForRemoveOnExit(win);
-                Binder.restoreCallingIdentity(origId);
-                return;
-            }
-            // If we are not currently running the exit animation, we need to see about starting one
-            wasVisible = win.isWinVisibleLw();
-
-            if (keepVisibleDeadWindow) {
-                if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
-                        "Not removing " + win + " because app died while it's visible");
-
-                win.mAppDied = true;
-                win.setDisplayLayoutNeeded();
-                mWindowPlacerLocked.performSurfacePlacement();
-
-                // Set up a replacement input channel since the app is now dead.
-                // We need to catch tapping on the dead window to restart the app.
-                win.openInputChannel(null);
-                mInputMonitor.updateInputWindowsLw(true /*force*/);
-
-                Binder.restoreCallingIdentity(origId);
-                return;
-            }
-
-            final WindowStateAnimator winAnimator = win.mWinAnimator;
-            if (wasVisible) {
-                final int transit = (!startingWindow) ? TRANSIT_EXIT : TRANSIT_PREVIEW_DONE;
-
-                // Try starting an animation.
-                if (winAnimator.applyAnimationLocked(transit, false)) {
-                    win.mAnimatingExit = true;
-                }
-                //TODO (multidisplay): Magnification is supported only for the default display.
-                if (mAccessibilityController != null
-                        && win.getDisplayId() == Display.DEFAULT_DISPLAY) {
-                    mAccessibilityController.onWindowTransitionLocked(win, transit);
-                }
-            }
-            final boolean isAnimating =
-                    winAnimator.isAnimationSet() && !winAnimator.isDummyAnimation();
-            final boolean lastWindowIsStartingWindow = startingWindow && appToken != null
-                    && appToken.allAppWindows.size() == 1;
-            // We delay the removal of a window if it has a showing surface that can be used to run
-            // exit animation and it is marked as exiting.
-            // Also, If isn't the an animating starting window that is the last window in the app.
-            // We allow the removal of the non-animating starting window now as there is no
-            // additional window or animation that will trigger its removal.
-            if (winAnimator.getShown() && win.mAnimatingExit
-                    && (!lastWindowIsStartingWindow || isAnimating)) {
-                // The exit animation is running or should run... wait for it!
-                if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
-                        "Not removing " + win + " due to exit animation ");
-                setupWindowForRemoveOnExit(win);
-                if (appToken != null) {
-                    appToken.updateReportedVisibilityLocked();
-                }
-                Binder.restoreCallingIdentity(origId);
-                return;
-            }
-        }
-
-        win.remove();
-        // Removing a visible window will effect the computed orientation
-        // So just update orientation if needed.
-        if (wasVisible && updateOrientationFromAppTokensLocked(false)) {
-            mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
-        }
-        updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
-        Binder.restoreCallingIdentity(origId);
+        win.removeIfPossible(false /*keepVisibleDeadWindow*/);
     }
 
     /**
@@ -2063,42 +1911,26 @@
         final AppWindowToken atoken = win.mAppToken;
         if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "Removing " + win + " from " + token);
         token.removeWindow(win);
-        if (atoken != null) {
-            atoken.allAppWindows.remove(win);
-        }
         if (token.isEmpty()) {
             if (!token.explicit) {
                 mTokenMap.remove(token.token);
             } else if (atoken != null) {
+                // TODO: Should this be moved into AppWindowToken.removeWindow? Might go away after
+                // re-factor.
                 atoken.firstWindowDrawn = false;
                 atoken.clearAllDrawn();
             }
         }
 
         if (atoken != null) {
-            if (atoken.startingWindow == win) {
-                if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Notify removed startingWindow " + win);
-                scheduleRemoveStartingWindowLocked(atoken);
-            } else
-            if (atoken.allAppWindows.size() == 0 && atoken.startingData != null) {
-                // If this is the last window and we had requested a starting
-                // transition window, well there is no point now.
-                if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Nulling last startingWindow");
-                atoken.startingData = null;
-            } else if (atoken.allAppWindows.size() == 1 && atoken.startingView != null) {
-                // If this is the last window except for a starting transition
-                // window, we need to get rid of the starting transition.
-                scheduleRemoveStartingWindowLocked(atoken);
-            }
+            atoken.removeWindow(win);
         }
 
         if (win.mAttrs.type == TYPE_WALLPAPER) {
             mWallpaperControllerLocked.clearLastWallpaperTimeoutTime();
-            getDefaultDisplayContentLocked().pendingLayoutChanges |=
-                    WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
-        } else if ((win.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0) {
-            getDefaultDisplayContentLocked().pendingLayoutChanges |=
-                    WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+            getDefaultDisplayContentLocked().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+        } else if ((win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) {
+            getDefaultDisplayContentLocked().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
         }
 
         final WindowList windows = win.getWindowList();
@@ -2735,7 +2567,7 @@
         }
     }
 
-    private boolean applyAnimationLocked(AppWindowToken atoken, WindowManager.LayoutParams lp,
+    boolean applyAnimationLocked(AppWindowToken atoken, WindowManager.LayoutParams lp,
             int transit, boolean enter, boolean isVoiceInteraction) {
         // Only apply an animation if the display isn't frozen.  If it is
         // frozen, there is no reason to animate and it can cause strange
@@ -3231,9 +3063,9 @@
 
         if (updateOrientationFromAppTokensLocked(false)) {
             if (freezeThisOneIfNeeded != null) {
-                AppWindowToken atoken = findAppWindowToken(freezeThisOneIfNeeded);
+                final AppWindowToken atoken = findAppWindowToken(freezeThisOneIfNeeded);
                 if (atoken != null) {
-                    startAppFreezingScreenLocked(atoken);
+                    atoken.startFreezingScreen();
                 }
             }
             config = computeNewConfigurationLocked();
@@ -3677,7 +3509,7 @@
                 }
             }
 
-            if (transferStartingWindow(transferFrom, wtoken)) {
+            if (wtoken != null && wtoken.transferStartingWindow(transferFrom)) {
                 return true;
             }
 
@@ -3700,108 +3532,6 @@
         return true;
     }
 
-    private boolean transferStartingWindow(IBinder transferFrom, AppWindowToken wtoken) {
-        if (transferFrom == null) {
-            return false;
-        }
-        AppWindowToken ttoken = findAppWindowToken(transferFrom);
-        if (ttoken == null) {
-            return false;
-        }
-        WindowState startingWindow = ttoken.startingWindow;
-        if (startingWindow != null && ttoken.startingView != null) {
-            // In this case, the starting icon has already been displayed, so start
-            // letting windows get shown immediately without any more transitions.
-            mSkipAppTransitionAnimation = true;
-
-            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
-                    "Moving existing starting " + startingWindow + " from " + ttoken
-                            + " to " + wtoken);
-            final long origId = Binder.clearCallingIdentity();
-
-            // Transfer the starting window over to the new token.
-            wtoken.startingData = ttoken.startingData;
-            wtoken.startingView = ttoken.startingView;
-            wtoken.startingDisplayed = ttoken.startingDisplayed;
-            ttoken.startingDisplayed = false;
-            wtoken.startingWindow = startingWindow;
-            wtoken.reportedVisible = ttoken.reportedVisible;
-            ttoken.startingData = null;
-            ttoken.startingView = null;
-            ttoken.startingWindow = null;
-            ttoken.startingMoved = true;
-            startingWindow.mToken = wtoken;
-            startingWindow.mRootToken = wtoken;
-            startingWindow.mAppToken = wtoken;
-
-            if (DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE || DEBUG_STARTING_WINDOW) {
-                Slog.v(TAG_WM, "Removing starting window: " + startingWindow);
-            }
-            startingWindow.getWindowList().remove(startingWindow);
-            mWindowsChanged = true;
-            if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
-                    "Removing starting " + startingWindow + " from " + ttoken);
-            ttoken.removeWindow(startingWindow);
-            ttoken.allAppWindows.remove(startingWindow);
-            wtoken.addWindowToList(startingWindow);
-
-            // Propagate other interesting state between the tokens. If the old token is displayed,
-            // we should immediately force the new one to be displayed. If it is animating, we need
-            // to move that animation to the new one.
-            if (ttoken.allDrawn) {
-                wtoken.allDrawn = true;
-                wtoken.deferClearAllDrawn = ttoken.deferClearAllDrawn;
-            }
-            if (ttoken.firstWindowDrawn) {
-                wtoken.firstWindowDrawn = true;
-            }
-            if (!ttoken.hidden) {
-                wtoken.hidden = false;
-                wtoken.hiddenRequested = false;
-            }
-            if (wtoken.clientHidden != ttoken.clientHidden) {
-                wtoken.clientHidden = ttoken.clientHidden;
-                wtoken.sendAppVisibilityToClients();
-            }
-            ttoken.mAppAnimator.transferCurrentAnimation(
-                    wtoken.mAppAnimator, startingWindow.mWinAnimator);
-
-            updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
-                    true /*updateInputWindows*/);
-            getDefaultDisplayContentLocked().layoutNeeded = true;
-            mWindowPlacerLocked.performSurfacePlacement();
-            Binder.restoreCallingIdentity(origId);
-            return true;
-        } else if (ttoken.startingData != null) {
-            // The previous app was getting ready to show a
-            // starting window, but hasn't yet done so.  Steal it!
-            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Moving pending starting from " + ttoken
-                    + " to " + wtoken);
-            wtoken.startingData = ttoken.startingData;
-            ttoken.startingData = null;
-            ttoken.startingMoved = true;
-            Message m = mH.obtainMessage(H.ADD_STARTING, wtoken);
-            // Note: we really want to do sendMessageAtFrontOfQueue() because we
-            // want to process the message ASAP, before any other queued
-            // messages.
-            mH.sendMessageAtFrontOfQueue(m);
-            return true;
-        }
-        final AppWindowAnimator tAppAnimator = ttoken.mAppAnimator;
-        final AppWindowAnimator wAppAnimator = wtoken.mAppAnimator;
-        if (tAppAnimator.thumbnail != null) {
-            // The old token is animating with a thumbnail, transfer that to the new token.
-            if (wAppAnimator.thumbnail != null) {
-                wAppAnimator.thumbnail.destroy();
-            }
-            wAppAnimator.thumbnail = tAppAnimator.thumbnail;
-            wAppAnimator.thumbnailLayer = tAppAnimator.thumbnailLayer;
-            wAppAnimator.thumbnailAnimation = tAppAnimator.thumbnailAnimation;
-            tAppAnimator.thumbnail = null;
-        }
-        return false;
-    }
-
     public void removeAppStartingWindow(IBinder token) {
         synchronized (mWindowMap) {
             final AppWindowToken wtoken = mTokenMap.get(token).appWindowToken;
@@ -3836,155 +3566,8 @@
         }
     }
 
-    boolean setTokenVisibilityLocked(AppWindowToken wtoken, WindowManager.LayoutParams lp,
-            boolean visible, int transit, boolean performLayout, boolean isVoiceInteraction) {
-        boolean delayed = false;
-
-        if (wtoken.clientHidden == visible) {
-            wtoken.clientHidden = !visible;
-            wtoken.sendAppVisibilityToClients();
-        }
-
-        // Allow for state changes and animation to be applied if:
-        // * token is transitioning visibility state
-        // * or the token was marked as hidden and is exiting before we had a chance to play the
-        // transition animation
-        // * or this is an opening app and windows are being replaced.
-        boolean visibilityChanged = false;
-        if (wtoken.hidden == visible || (wtoken.hidden && wtoken.mIsExiting) ||
-                (visible && wtoken.waitingForReplacement())) {
-            boolean changed = false;
-            if (DEBUG_APP_TRANSITIONS) Slog.v(
-                TAG_WM, "Changing app " + wtoken + " hidden=" + wtoken.hidden
-                + " performLayout=" + performLayout);
-
-            boolean runningAppAnimation = false;
-
-            if (transit != AppTransition.TRANSIT_UNSET) {
-                if (wtoken.mAppAnimator.animation == AppWindowAnimator.sDummyAnimation) {
-                    wtoken.mAppAnimator.setNullAnimation();
-                }
-                if (applyAnimationLocked(wtoken, lp, transit, visible, isVoiceInteraction)) {
-                    delayed = runningAppAnimation = true;
-                }
-                WindowState window = wtoken.findMainWindow();
-                //TODO (multidisplay): Magnification is supported only for the default display.
-                if (window != null && mAccessibilityController != null
-                        && window.getDisplayId() == Display.DEFAULT_DISPLAY) {
-                    mAccessibilityController.onAppWindowTransitionLocked(window, transit);
-                }
-                changed = true;
-            }
-
-            final int windowsCount = wtoken.allAppWindows.size();
-            for (int i = 0; i < windowsCount; i++) {
-                WindowState win = wtoken.allAppWindows.get(i);
-                if (win == wtoken.startingWindow) {
-                    // Starting window that's exiting will be removed when the animation
-                    // finishes. Mark all relevant flags for that onExitAnimationDone will proceed
-                    // all the way to actually remove it.
-                    if (!visible && win.isVisibleNow() && wtoken.mAppAnimator.isAnimating()) {
-                        win.mAnimatingExit = true;
-                        win.mRemoveOnExit = true;
-                        win.mWindowRemovalAllowed = true;
-                    }
-                    continue;
-                }
-
-                //Slog.i(TAG_WM, "Window " + win + ": vis=" + win.isVisible());
-                //win.dump("  ");
-                if (visible) {
-                    if (!win.isVisibleNow()) {
-                        if (!runningAppAnimation) {
-                            win.mWinAnimator.applyAnimationLocked(
-                                    WindowManagerPolicy.TRANSIT_ENTER, true);
-                            //TODO (multidisplay): Magnification is supported only for the default
-                            if (mAccessibilityController != null
-                                    && win.getDisplayId() == Display.DEFAULT_DISPLAY) {
-                                mAccessibilityController.onWindowTransitionLocked(win,
-                                        WindowManagerPolicy.TRANSIT_ENTER);
-                            }
-                        }
-                        changed = true;
-                        win.setDisplayLayoutNeeded();
-                    }
-                } else if (win.isVisibleNow()) {
-                    if (!runningAppAnimation) {
-                        win.mWinAnimator.applyAnimationLocked(
-                                WindowManagerPolicy.TRANSIT_EXIT, false);
-                        //TODO (multidisplay): Magnification is supported only for the default
-                        if (mAccessibilityController != null
-                                && win.getDisplayId() == Display.DEFAULT_DISPLAY) {
-                            mAccessibilityController.onWindowTransitionLocked(win,
-                                    WindowManagerPolicy.TRANSIT_EXIT);
-                        }
-                    }
-                    changed = true;
-                    win.setDisplayLayoutNeeded();
-                }
-            }
-
-            wtoken.hidden = wtoken.hiddenRequested = !visible;
-            visibilityChanged = true;
-            if (!visible) {
-                unsetAppFreezingScreenLocked(wtoken, true, true);
-            } else {
-                // If we are being set visible, and the starting window is
-                // not yet displayed, then make sure it doesn't get displayed.
-                WindowState swin = wtoken.startingWindow;
-                if (swin != null && !swin.isDrawnLw()) {
-                    swin.mPolicyVisibility = false;
-                    swin.mPolicyVisibilityAfterAnim = false;
-                 }
-            }
-
-            if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "setTokenVisibilityLocked: " + wtoken
-                      + ": hidden=" + wtoken.hidden + " hiddenRequested="
-                      + wtoken.hiddenRequested);
-
-            if (changed) {
-                mInputMonitor.setUpdateInputWindowsNeededLw();
-                if (performLayout) {
-                    updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
-                            false /*updateInputWindows*/);
-                    mWindowPlacerLocked.performSurfacePlacement();
-                }
-                mInputMonitor.updateInputWindowsLw(false /*force*/);
-            }
-        }
-
-        if (wtoken.mAppAnimator.animation != null) {
-            delayed = true;
-        }
-
-        for (int i = wtoken.allAppWindows.size() - 1; i >= 0 && !delayed; i--) {
-            if (wtoken.allAppWindows.get(i).mWinAnimator.isWindowAnimationSet()) {
-                delayed = true;
-            }
-        }
-
-        if (visibilityChanged) {
-            if (visible && !delayed) {
-                // The token was made immediately visible, there will be no entrance animation.
-                // We need to inform the client the enter animation was finished.
-                wtoken.mEnteringAnimation = true;
-                mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(wtoken.token);
-            }
-
-            if (!mClosingApps.contains(wtoken) && !mOpeningApps.contains(wtoken)) {
-                // The token is not closing nor opening, so even if there is an animation set, that
-                // doesn't mean that it goes through the normal app transition cycle so we have
-                // to inform the docked controller about visibility change.
-                getDefaultDisplayContentLocked().getDockedDividerController()
-                        .notifyAppVisibilityChanged();
-            }
-        }
-
-        return delayed;
-    }
-
     void updateTokenInPlaceLocked(AppWindowToken wtoken, int transit) {
-        if (transit != AppTransition.TRANSIT_UNSET) {
+        if (transit != TRANSIT_UNSET) {
             if (wtoken.mAppAnimator.animation == AppWindowAnimator.sDummyAnimation) {
                 wtoken.mAppAnimator.setNullAnimation();
             }
@@ -4141,75 +3724,12 @@
             }
 
             final long origId = Binder.clearCallingIdentity();
-            wtoken.inPendingTransaction = false;
-            setTokenVisibilityLocked(wtoken, null, visible, AppTransition.TRANSIT_UNSET,
-                    true, wtoken.voiceInteraction);
+            wtoken.setVisibility(null, visible, TRANSIT_UNSET, true, wtoken.voiceInteraction);
             wtoken.updateReportedVisibilityLocked();
             Binder.restoreCallingIdentity(origId);
         }
     }
 
-    void unsetAppFreezingScreenLocked(AppWindowToken wtoken,
-            boolean unfreezeSurfaceNow, boolean force) {
-        if (wtoken.mAppAnimator.freezingScreen) {
-            if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Clear freezing of " + wtoken
-                    + " force=" + force);
-            final int N = wtoken.allAppWindows.size();
-            boolean unfrozeWindows = false;
-            for (int i=0; i<N; i++) {
-                WindowState w = wtoken.allAppWindows.get(i);
-                if (w.mAppFreezing) {
-                    w.mAppFreezing = false;
-                    if (w.mHasSurface && !w.mOrientationChanging
-                            && mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) {
-                        if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "set mOrientationChanging of " + w);
-                        w.mOrientationChanging = true;
-                        mWindowPlacerLocked.mOrientationChangeComplete = false;
-                    }
-                    w.mLastFreezeDuration = 0;
-                    unfrozeWindows = true;
-                    w.setDisplayLayoutNeeded();
-                }
-            }
-            if (force || unfrozeWindows) {
-                if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "No longer freezing: " + wtoken);
-                wtoken.mAppAnimator.freezingScreen = false;
-                wtoken.mAppAnimator.lastFreezeDuration = (int)(SystemClock.elapsedRealtime()
-                        - mDisplayFreezeTime);
-                mAppsFreezingScreen--;
-                mLastFinishedFreezeSource = wtoken;
-            }
-            if (unfreezeSurfaceNow) {
-                if (unfrozeWindows) {
-                    mWindowPlacerLocked.performSurfacePlacement();
-                }
-                stopFreezingDisplayLocked();
-            }
-        }
-    }
-
-    private void startAppFreezingScreenLocked(AppWindowToken wtoken) {
-        if (DEBUG_ORIENTATION) logWithStack(TAG, "Set freezing of " + wtoken.appToken + ": hidden="
-                + wtoken.hidden + " freezing=" + wtoken.mAppAnimator.freezingScreen);
-        if (!wtoken.hiddenRequested) {
-            if (!wtoken.mAppAnimator.freezingScreen) {
-                wtoken.mAppAnimator.freezingScreen = true;
-                wtoken.mAppAnimator.lastFreezeDuration = 0;
-                mAppsFreezingScreen++;
-                if (mAppsFreezingScreen == 1) {
-                    startFreezingDisplayLocked(false, 0, 0);
-                    mH.removeMessages(H.APP_FREEZE_TIMEOUT);
-                    mH.sendEmptyMessageDelayed(H.APP_FREEZE_TIMEOUT, 2000);
-                }
-            }
-            final int N = wtoken.allAppWindows.size();
-            for (int i=0; i<N; i++) {
-                WindowState w = wtoken.allAppWindows.get(i);
-                w.mAppFreezing = true;
-            }
-        }
-    }
-
     @Override
     public void startAppFreezingScreen(IBinder token, int configChanges) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
@@ -4223,13 +3743,13 @@
                 return;
             }
 
-            AppWindowToken wtoken = findAppWindowToken(token);
+            final AppWindowToken wtoken = findAppWindowToken(token);
             if (wtoken == null || wtoken.appToken == null) {
                 Slog.w(TAG_WM, "Attempted to freeze screen with non-existing app token: " + wtoken);
                 return;
             }
             final long origId = Binder.clearCallingIdentity();
-            startAppFreezingScreenLocked(wtoken);
+            wtoken.startFreezingScreen();
             Binder.restoreCallingIdentity(origId);
         }
     }
@@ -4249,7 +3769,7 @@
             final long origId = Binder.clearCallingIdentity();
             if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Clear freezing of " + token
                     + ": hidden=" + wtoken.hidden + " freezing=" + wtoken.mAppAnimator.freezingScreen);
-            unsetAppFreezingScreenLocked(wtoken, true, force);
+            wtoken.stopFreezingScreen(true, force);
             Binder.restoreCallingIdentity(origId);
         }
     }
@@ -4270,9 +3790,8 @@
             WindowToken basewtoken = mTokenMap.remove(token);
             if (basewtoken != null && (wtoken=basewtoken.appWindowToken) != null) {
                 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "Removing app token: " + wtoken);
-                delayed = setTokenVisibilityLocked(wtoken, null, false,
-                        AppTransition.TRANSIT_UNSET, true, wtoken.voiceInteraction);
-                wtoken.inPendingTransaction = false;
+                delayed = wtoken.setVisibility(null, false,
+                        TRANSIT_UNSET, true, wtoken.voiceInteraction);
                 mOpeningApps.remove(wtoken);
                 wtoken.waitingToShow = false;
                 if (mClosingApps.contains(wtoken)) {
@@ -4288,7 +3807,7 @@
                 if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG_WM, "removeAppToken: "
                         + wtoken + " delayed=" + delayed + " Callers=" + Debug.getCallers(4));
                 final TaskStack stack = wtoken.mTask.mStack;
-                if (delayed && !wtoken.allAppWindows.isEmpty()) {
+                if (delayed && !wtoken.isEmpty()) {
                     // set the token aside because it has an active animation to be finished
                     if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG_WM,
                             "removeAppToken make exiting: " + wtoken);
@@ -4307,7 +3826,7 @@
                 if (wtoken.startingData != null) {
                     startingToken = wtoken;
                 }
-                unsetAppFreezingScreenLocked(wtoken, true, true);
+                wtoken.stopFreezingScreen(true, true);
                 if (mFocusedApp == wtoken) {
                     if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Removing focused app token:" + wtoken);
                     mFocusedApp = null;
@@ -7909,7 +7428,7 @@
                                     AppWindowToken tok = tokens.get(tokenNdx);
                                     if (tok.mAppAnimator.freezingScreen) {
                                         Slog.w(TAG_WM, "Force clearing freeze: " + tok);
-                                        unsetAppFreezingScreenLocked(tok, true, true);
+                                        tok.stopFreezingScreen(true, true);
                                     }
                                 }
                             }
@@ -9292,7 +8811,7 @@
         return null;
     }
 
-    private void startFreezingDisplayLocked(boolean inTransaction, int exitAnim, int enterAnim) {
+    void startFreezingDisplayLocked(boolean inTransaction, int exitAnim, int enterAnim) {
         if (mDisplayFrozen) {
             return;
         }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 0c4ee7e..845f112 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -25,6 +25,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.os.Binder;
 import android.os.Debug;
 import android.os.IBinder;
 import android.os.PowerManager;
@@ -97,6 +98,8 @@
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static android.view.WindowManagerPolicy.TRANSIT_EXIT;
+import static android.view.WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
@@ -117,8 +120,12 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.H.SEND_NEW_CONFIGURATION;
 import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
 import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
+import static com.android.server.wm.WindowManagerService.localLOGV;
 import static com.android.server.wm.WindowStateAnimator.COMMIT_DRAW_PENDING;
 import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
 import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW;
@@ -570,7 +577,7 @@
         DeathRecipient deathRecipient = new DeathRecipient();
         mSeq = seq;
         mEnforceSizeCompat = (mAttrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0;
-        if (WindowManagerService.localLOGV) Slog.v(
+        if (localLOGV) Slog.v(
             TAG, "Window " + this + " client=" + c.asBinder()
             + " token=" + token + " (" + mAttrs.token + ")" + " params=" + a);
         try {
@@ -658,7 +665,7 @@
     }
 
     void attach() {
-        if (WindowManagerService.localLOGV) Slog.v(TAG, "Attaching " + this + " token=" + mToken);
+        if (localLOGV) Slog.v(TAG, "Attaching " + this + " token=" + mToken);
         mSession.windowAddedLocked();
     }
 
@@ -943,7 +950,7 @@
             }
         }
 
-        if (DEBUG_LAYOUT || WindowManagerService.localLOGV) Slog.v(TAG,
+        if (DEBUG_LAYOUT || localLOGV) Slog.v(TAG,
                 "Resolving (mRequestedWidth="
                 + mRequestedWidth + ", mRequestedheight="
                 + mRequestedHeight + ") to" + " (pw=" + pw + ", ph=" + ph
@@ -1489,6 +1496,154 @@
         mService.postWindowRemoveCleanupLocked(this);
     }
 
+    void removeIfPossible(boolean keepVisibleDeadWindow) {
+        mWindowRemovalAllowed = true;
+        if (DEBUG_ADD_REMOVE) Slog.v(TAG,
+                "removeIfPossible: " + this + " callers=" + Debug.getCallers(5));
+
+        final boolean startingWindow = mAttrs.type == TYPE_APPLICATION_STARTING;
+        if (startingWindow && DEBUG_STARTING_WINDOW) Slog.d(TAG_WM,
+                "Starting window removed " + this);
+
+        if (localLOGV || DEBUG_FOCUS || DEBUG_FOCUS_LIGHT && this == mService.mCurrentFocus)
+            Slog.v(TAG_WM, "Remove " + this + " client="
+                        + Integer.toHexString(System.identityHashCode(mClient.asBinder()))
+                        + ", surfaceController=" + mWinAnimator.mSurfaceController + " Callers="
+                        + Debug.getCallers(5));
+
+        final long origId = Binder.clearCallingIdentity();
+
+        disposeInputChannel();
+
+        if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "Remove " + this
+                + ": mSurfaceController=" + mWinAnimator.mSurfaceController
+                + " mAnimatingExit=" + mAnimatingExit
+                + " mRemoveOnExit=" + mRemoveOnExit
+                + " mHasSurface=" + mHasSurface
+                + " surfaceShowing=" + mWinAnimator.getShown()
+                + " isAnimationSet=" + mWinAnimator.isAnimationSet()
+                + " app-animation="
+                + (mAppToken != null ? mAppToken.mAppAnimator.animation : null)
+                + " mWillReplaceWindow=" + mWillReplaceWindow
+                + " inPendingTransaction="
+                + (mAppToken != null ? mAppToken.inPendingTransaction : false)
+                + " mDisplayFrozen=" + mService.mDisplayFrozen
+                + " callers=" + Debug.getCallers(6));
+
+        // Visibility of the removed window. Will be used later to update orientation later on.
+        boolean wasVisible = false;
+
+        // First, see if we need to run an animation. If we do, we have to hold off on removing the
+        // window until the animation is done. If the display is frozen, just remove immediately,
+        // since the animation wouldn't be seen.
+        if (mHasSurface && mService.okToDisplay()) {
+            if (mWillReplaceWindow) {
+                // This window is going to be replaced. We need to keep it around until the new one
+                // gets added, then we will get rid of this one.
+                if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
+                        "Preserving " + this + " until the new one is " + "added");
+                // TODO: We are overloading mAnimatingExit flag to prevent the window state from
+                // been removed. We probably need another flag to indicate that window removal
+                // should be deffered vs. overloading the flag that says we are playing an exit
+                // animation.
+                mAnimatingExit = true;
+                mReplacingRemoveRequested = true;
+                Binder.restoreCallingIdentity(origId);
+                return;
+            }
+
+            if (isAnimatingWithSavedSurface() && !mAppToken.allDrawnExcludingSaved) {
+                // We started enter animation early with a saved surface, now the app asks to remove
+                // this window. If we remove it now and the app is not yet drawn, we'll show a
+                // flicker. Delay the removal now until it's really drawn.
+                if (DEBUG_ADD_REMOVE) Slog.d(TAG_WM,
+                        "removeWindowLocked: delay removal of " + this + " due to early animation");
+                // Do not set mAnimatingExit to true here, it will cause the surface to be hidden
+                // immediately after the enter animation is done. If the app is not yet drawn then
+                // it will show up as a flicker.
+                setupWindowForRemoveOnExit();
+                Binder.restoreCallingIdentity(origId);
+                return;
+            }
+            // If we are not currently running the exit animation, we need to see about starting one
+            wasVisible = isWinVisibleLw();
+
+            if (keepVisibleDeadWindow) {
+                if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
+                        "Not removing " + this + " because app died while it's visible");
+
+                mAppDied = true;
+                setDisplayLayoutNeeded();
+                mService.mWindowPlacerLocked.performSurfacePlacement();
+
+                // Set up a replacement input channel since the app is now dead.
+                // We need to catch tapping on the dead window to restart the app.
+                openInputChannel(null);
+                mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
+
+                Binder.restoreCallingIdentity(origId);
+                return;
+            }
+
+            if (wasVisible) {
+                final int transit = (!startingWindow) ? TRANSIT_EXIT : TRANSIT_PREVIEW_DONE;
+
+                // Try starting an animation.
+                if (mWinAnimator.applyAnimationLocked(transit, false)) {
+                    mAnimatingExit = true;
+                }
+                //TODO (multidisplay): Magnification is supported only for the default display.
+                if (mService.mAccessibilityController != null
+                        && getDisplayId() == Display.DEFAULT_DISPLAY) {
+                    mService.mAccessibilityController.onWindowTransitionLocked(this, transit);
+                }
+            }
+            final boolean isAnimating =
+                    mWinAnimator.isAnimationSet() && !mWinAnimator.isDummyAnimation();
+            final boolean lastWindowIsStartingWindow = startingWindow && mAppToken != null
+                    && mAppToken.getWindowsCount() == 1;
+            // We delay the removal of a window if it has a showing surface that can be used to run
+            // exit animation and it is marked as exiting.
+            // Also, If isn't the an animating starting window that is the last window in the app.
+            // We allow the removal of the non-animating starting window now as there is no
+            // additional window or animation that will trigger its removal.
+            if (mWinAnimator.getShown() && mAnimatingExit
+                    && (!lastWindowIsStartingWindow || isAnimating)) {
+                // The exit animation is running or should run... wait for it!
+                if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
+                        "Not removing " + this + " due to exit animation ");
+                setupWindowForRemoveOnExit();
+                if (mAppToken != null) {
+                    mAppToken.updateReportedVisibilityLocked();
+                }
+                Binder.restoreCallingIdentity(origId);
+                return;
+            }
+        }
+
+        remove();
+        // Removing a visible window will effect the computed orientation
+        // So just update orientation if needed.
+        if (wasVisible && mService.updateOrientationFromAppTokensLocked(false)) {
+            mService.mH.sendEmptyMessage(SEND_NEW_CONFIGURATION);
+        }
+        mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
+        Binder.restoreCallingIdentity(origId);
+    }
+
+    private void setupWindowForRemoveOnExit() {
+        mRemoveOnExit = true;
+        setDisplayLayoutNeeded();
+        // Request a focus update as this window's input channel is already gone. Otherwise
+        // we could have no focused window in input manager.
+        final boolean focusChanged = mService.updateFocusedWindowLocked(
+                UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
+        mService.mWindowPlacerLocked.performSurfacePlacement();
+        if (focusChanged) {
+            mService.mInputMonitor.updateInputWindowsLw(false /*force*/);
+        }
+    }
+
     void setHasSurface(boolean hasSurface) {
         mHasSurface = hasSurface;
     }
@@ -1637,27 +1792,17 @@
         return getStack();
     }
 
-    void maybeRemoveReplacedWindow() {
-        if (mAppToken == null) {
-            return;
+    void removeReplacedWindow() {
+        if (DEBUG_ADD_REMOVE) Slog.d(TAG, "Removing replaced window: " + this);
+        if (isDimming()) {
+            transferDimToReplacement();
         }
-        for (int i = mAppToken.allAppWindows.size() - 1; i >= 0; i--) {
-            final WindowState win = mAppToken.allAppWindows.get(i);
-            if (win.mWillReplaceWindow && win.mReplacingWindow == this && hasDrawnLw()) {
-                if (DEBUG_ADD_REMOVE) Slog.d(TAG, "Removing replaced window: " + win);
-                if (win.isDimming()) {
-                    win.transferDimToReplacement();
-                }
-                win.mWillReplaceWindow = false;
-                final boolean animateReplacingWindow = win.mAnimateReplacingWindow;
-                win.mAnimateReplacingWindow = false;
-                win.mReplacingRemoveRequested = false;
-                win.mReplacingWindow = null;
-                mSkipEnterAnimationForSeamlessReplacement = false;
-                if (win.mAnimatingExit || !animateReplacingWindow) {
-                    win.remove();
-                }
-            }
+        mWillReplaceWindow = false;
+        mAnimateReplacingWindow = false;
+        mReplacingRemoveRequested = false;
+        mReplacingWindow = null;
+        if (mAnimatingExit || !mAnimateReplacingWindow) {
+            remove();
         }
     }
 
@@ -1797,10 +1942,10 @@
         public void binderDied() {
             try {
                 synchronized(mService.mWindowMap) {
-                    WindowState win = mService.windowForClientLocked(mSession, mClient, false);
+                    final WindowState win = mService.windowForClientLocked(mSession, mClient, false);
                     Slog.i(TAG, "WIN DEATH: " + win);
                     if (win != null) {
-                        mService.removeWindowLocked(win, shouldKeepVisibleDeadAppWindow());
+                        win.removeIfPossible(shouldKeepVisibleDeadAppWindow());
                         if (win.mAttrs.type == TYPE_DOCK_DIVIDER) {
                             // The owner of the docked divider died :( We reset the docked stack,
                             // just in case they have the divider at an unstable position. Better
@@ -3178,7 +3323,7 @@
             return;
         }
 
-        if (WindowManagerService.localLOGV || DEBUG_ADD_REMOVE) Slog.v(TAG,
+        if (localLOGV || DEBUG_ADD_REMOVE) Slog.v(TAG,
                 "Exit animation finished in " + this + ": remove=" + mRemoveOnExit);
 
         mDestroying = true;
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 677b4af..25709c5 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -453,7 +453,7 @@
             for (i = exitingAppTokens.size() - 1; i >= 0; i--) {
                 AppWindowToken token = exitingAppTokens.get(i);
                 if (!token.hasVisible && !mService.mClosingApps.contains(token) &&
-                        (!token.mIsExiting || token.allAppWindows.isEmpty())) {
+                        (!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
@@ -1248,10 +1248,8 @@
                 appAnimator.clearThumbnail();
                 appAnimator.setNullAnimation();
             }
-            wtoken.inPendingTransaction = false;
 
-            if (!mService.setTokenVisibilityLocked(
-                    wtoken, animLp, true, transit, false, voiceInteraction)){
+            if (!wtoken.setVisibility(animLp, true, transit, false, voiceInteraction)){
                 // This token isn't going to be animating. Add it to the list of tokens to
                 // be notified of app transition complete since the notification will not be
                 // sent be the app window animator.
@@ -1259,12 +1257,8 @@
             }
             wtoken.updateReportedVisibilityLocked();
             wtoken.waitingToShow = false;
+            wtoken.setAllAppWinAnimators();
 
-            appAnimator.mAllAppWinAnimators.clear();
-            final int windowsCount = wtoken.allAppWindows.size();
-            for (int j = 0; j < windowsCount; j++) {
-                appAnimator.mAllAppWinAnimators.add(wtoken.allAppWindows.get(j).mWinAnimator);
-            }
             if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
                     ">>> OPEN TRANSACTION handleAppTransitionReadyLocked()");
             SurfaceControl.openTransaction();
@@ -1279,13 +1273,7 @@
 
             int topOpeningLayer = 0;
             if (animLp != null) {
-                int layer = -1;
-                for (int j = 0; j < wtoken.allAppWindows.size(); j++) {
-                    final WindowState win = wtoken.allAppWindows.get(j);
-                    if (win.mWinAnimator.mAnimLayer > layer) {
-                        layer = win.mWinAnimator.mAnimLayer;
-                    }
-                }
+                final int layer = wtoken.getHighestAnimLayer();
                 if (topOpeningApp == null || layer > topOpeningLayer) {
                     topOpeningApp = wtoken;
                     topOpeningLayer = layer;
@@ -1316,9 +1304,7 @@
             if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now closing app " + wtoken);
             appAnimator.clearThumbnail();
             appAnimator.setNullAnimation();
-            wtoken.inPendingTransaction = false;
-            mService.setTokenVisibilityLocked(wtoken, animLp, false, transit, false,
-                    voiceInteraction);
+            wtoken.setVisibility(animLp, false, transit, false, voiceInteraction);
             wtoken.updateReportedVisibilityLocked();
             // Force the allDrawn flag, because we want to start
             // this guy's animations regardless of whether it's
@@ -1604,12 +1590,7 @@
                 appAnimator.setNullAnimation();
                 mService.updateTokenInPlaceLocked(wtoken, transit);
                 wtoken.updateReportedVisibilityLocked();
-
-                appAnimator.mAllAppWinAnimators.clear();
-                final int N = wtoken.allAppWindows.size();
-                for (int j = 0; j < N; j++) {
-                    appAnimator.mAllAppWinAnimators.add(wtoken.allAppWindows.get(j).mWinAnimator);
-                }
+                wtoken.setAllAppWinAnimators();
                 mService.mAnimator.mAppWindowAnimating |= appAnimator.isAnimating();
                 mService.mAnimator.orAnimating(appAnimator.showAllWindowsLocked());
             }
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 53ea981..c421cea 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -148,13 +148,19 @@
         }
     }
 
-    void adjustAnimLayer(int adj) {
+    int adjustAnimLayer(int adj) {
+        int highestAnimLayer = -1;
         for (int j = windows.size() - 1; j >= 0; j--) {
             final WindowState w = windows.get(j);
             w.adjustAnimLayer(adj);
-            if (DEBUG_LAYERS || DEBUG_WALLPAPER) Slog.v(TAG, "adjustAnimLayer win "
-                    + w + " anim layer: " + w.mWinAnimator.mAnimLayer);
+            final int animLayer = w.mWinAnimator.mAnimLayer;
+            if (DEBUG_LAYERS || DEBUG_WALLPAPER) Slog.v(TAG,
+                    "adjustAnimLayer win " + w + " anim layer: " + animLayer);
+            if (animLayer > highestAnimLayer) {
+                highestAnimLayer = animLayer;
+            }
         }
+        return highestAnimLayer;
     }
 
     private WindowState getTopWindow() {
@@ -274,7 +280,7 @@
         }
 
         final AppWindowToken appToken = win.mAppToken;
-        if (appToken != null && !appToken.allAppWindows.contains(win)) {
+        if (appToken != null) {
             appToken.addWindow(win);
         }
     }