Removed child windows from WindowToken window list

With the window container hierarchy model, containers should only
link to their direct children and delegate any operation on
decendants of their children to their children.

Bug: 30060889
Change-Id: I99ca0d181d54cfe75bbe24c1b0daaa06cf7cfd9a
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 0a47e40..8e0fcc1 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2464,7 +2464,7 @@
             // entrance of the new window to be properly animated.
             // Note here we always set the replacing window first, as the flags might be needed
             // during the relaunch. If we end up not doing any relaunch, we clear the flags later.
-            mWindowManager.setReplacingWindow(topActivity.appToken, animate);
+            mWindowManager.setWillReplaceWindow(topActivity.appToken, animate);
         }
 
         mWindowManager.deferSurfaceLayout();
@@ -2506,7 +2506,7 @@
             // If we didn't actual do a relaunch (indicated by kept==true meaning we kept the old
             // window), we need to clear the replace window settings. Otherwise, we schedule a
             // timeout to remove the old window if the replacing window is not coming in time.
-            mWindowManager.scheduleClearReplacingWindowIfNeeded(topActivity.appToken, !kept);
+            mWindowManager.scheduleClearWillReplaceWindows(topActivity.appToken, !kept);
         }
 
         if (!deferResume) {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index df212bc..66fa976 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -20,15 +20,11 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 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;
@@ -37,7 +33,6 @@
 import static com.android.server.wm.WindowManagerService.H.NOTIFY_ACTIVITY_DRAWN;
 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE;
 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;
@@ -50,7 +45,6 @@
 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;
@@ -132,6 +126,8 @@
     boolean startingDisplayed;
     boolean startingMoved;
     boolean firstWindowDrawn;
+    private final WindowState.UpdateReportedVisibilityResults mReportedVisibilityResults =
+            new WindowState.UpdateReportedVisibilityResults();
 
     // Input application handle used by the input dispatcher.
     final InputApplicationHandle mInputApplicationHandle;
@@ -162,19 +158,10 @@
     }
 
     void sendAppVisibilityToClients() {
-        final int N = windows.size();
-        for (int i=0; i<N; i++) {
-            WindowState win = windows.get(i);
-            if (win == startingWindow && clientHidden) {
-                // Don't hide the starting window.
-                continue;
-            }
-            try {
-                if (DEBUG_VISIBILITY) Slog.v(TAG,
-                        "Setting visibility of " + win + ": " + (!clientHidden));
-                win.mClient.dispatchAppVisibility(!clientHidden);
-            } catch (RemoteException e) {
-            }
+        final int count = windows.size();
+        for (int i = 0; i < count; i++) {
+            final WindowState win = windows.get(i);
+            win.sendAppVisibilityToClients(clientHidden);
         }
     }
 
@@ -189,7 +176,7 @@
         firstWindowDrawn = true;
 
         // We now have a good window to show, remove dead placeholders
-        removeAllDeadWindows();
+        removeDeadWindows();
 
         if (startingData != null) {
             if (DEBUG_STARTING_WINDOW || DEBUG_ANIM) Slog.v(TAG, "Finish starting "
@@ -209,50 +196,21 @@
             return;
         }
 
-        int numInteresting = 0;
-        int numVisible = 0;
-        int numDrawn = 0;
-        boolean nowGone = true;
+        if (DEBUG_VISIBILITY) Slog.v(TAG, "Update reported visibility: " + this);
+        final int count = windows.size();
 
-        if (DEBUG_VISIBILITY) Slog.v(TAG,
-                "Update reported visibility: " + this);
-        final int N = windows.size();
-        for (int i=0; i<N; i++) {
-            WindowState win = windows.get(i);
-            if (win == startingWindow || win.mAppFreezing
-                    || win.mViewVisibility != View.VISIBLE
-                    || win.mAttrs.type == TYPE_APPLICATION_STARTING
-                    || win.mDestroying) {
-                continue;
-            }
-            if (DEBUG_VISIBILITY) {
-                Slog.v(TAG, "Win " + win + ": isDrawn="
-                        + win.isDrawnLw()
-                        + ", isAnimationSet=" + win.mWinAnimator.isAnimationSet());
-                if (!win.isDrawnLw()) {
-                    Slog.v(TAG, "Not displayed: s=" +
-                            win.mWinAnimator.mSurfaceController
-                            + " pv=" + win.mPolicyVisibility
-                            + " mDrawState=" + win.mWinAnimator.mDrawState
-                            + " ph=" + win.isParentWindowHidden()
-                            + " th="
-                            + (win.mAppToken != null
-                                    ? win.mAppToken.hiddenRequested : false)
-                            + " a=" + win.mWinAnimator.mAnimating);
-                }
-            }
-            numInteresting++;
-            if (win.isDrawnLw()) {
-                numDrawn++;
-                if (!win.mWinAnimator.isAnimationSet()) {
-                    numVisible++;
-                }
-                nowGone = false;
-            } else if (win.mWinAnimator.isAnimationSet()) {
-                nowGone = false;
-            }
+        mReportedVisibilityResults.reset();
+
+        for (int i = 0; i < count; i++) {
+            final WindowState win = windows.get(i);
+            win.updateReportedVisibility(mReportedVisibilityResults);
         }
 
+        int numInteresting = mReportedVisibilityResults.numInteresting;
+        int numVisible = mReportedVisibilityResults.numVisible;
+        int numDrawn = mReportedVisibilityResults.numDrawn;
+        boolean nowGone = mReportedVisibilityResults.nowGone;
+
         boolean nowDrawn = numInteresting > 0 && numDrawn >= numInteresting;
         boolean nowVisible = numInteresting > 0 && numVisible >= numInteresting;
         if (!nowGone) {
@@ -268,23 +226,16 @@
                 + numInteresting + " visible=" + numVisible);
         if (nowDrawn != reportedDrawn) {
             if (nowDrawn) {
-                Message m = mService.mH.obtainMessage(
-                        H.REPORT_APPLICATION_TOKEN_DRAWN, this);
-                mService.mH.sendMessage(m);
+                mService.mH.obtainMessage(H.REPORT_APPLICATION_TOKEN_DRAWN, this).sendToTarget();
             }
             reportedDrawn = nowDrawn;
         }
         if (nowVisible != reportedVisible) {
-            if (DEBUG_VISIBILITY) Slog.v(
-                    TAG, "Visibility changed in " + this
-                    + ": vis=" + nowVisible);
+            if (DEBUG_VISIBILITY) Slog.v(TAG,
+                    "Visibility changed in " + this + ": vis=" + nowVisible);
             reportedVisible = nowVisible;
-            Message m = mService.mH.obtainMessage(
-                    H.REPORT_APPLICATION_TOKEN_WINDOWS,
-                    nowVisible ? 1 : 0,
-                    nowGone ? 1 : 0,
-                    this);
-            mService.mH.sendMessage(m);
+            mService.mH.obtainMessage(H.REPORT_APPLICATION_TOKEN_WINDOWS,
+                    nowVisible ? 1 : 0, nowGone ? 1 : 0, this).sendToTarget();
         }
     }
 
@@ -332,45 +283,7 @@
             final int windowsCount = windows.size();
             for (int i = 0; i < windowsCount; i++) {
                 final WindowState win = windows.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();
-                }
+                changed |= win.onAppVisibilityChanged(visible, runningAppAnimation);
             }
 
             hidden = hiddenRequested = !visible;
@@ -378,12 +291,11 @@
             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 we are being set visible, and the starting window is not yet displayed,
+                // then make sure it doesn't get displayed.
+                if (startingWindow != null && !startingWindow.isDrawnLw()) {
+                    startingWindow.mPolicyVisibility = false;
+                    startingWindow.mPolicyVisibilityAfterAnim = false;
                 }
             }
 
@@ -406,7 +318,7 @@
         }
 
         for (int i = windows.size() - 1; i >= 0 && !delayed; i--) {
-            if (windows.get(i).mWinAnimator.isWindowAnimationSet()) {
+            if (windows.get(i).isWindowAnimationSet()) {
                 delayed = true;
             }
         }
@@ -436,9 +348,11 @@
         int j = windows.size();
         while (j > 0) {
             j--;
-            WindowState win = windows.get(j);
-            if (win.mAttrs.type == WindowManager.LayoutParams.TYPE_BASE_APPLICATION
-                    || win.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING) {
+            final WindowState win = windows.get(j);
+            final int type = win.mAttrs.type;
+            // No need to loop through child window as base application and starting types can't be
+            // child windows.
+            if (type == TYPE_BASE_APPLICATION || type == TYPE_APPLICATION_STARTING) {
                 // In cases where there are multiple windows, we prefer the non-exiting window. This
                 // happens for example when replacing windows during an activity relaunch. When
                 // constructing the animation, we want the new window, not the exiting one.
@@ -457,24 +371,17 @@
     }
 
     boolean isVisible() {
-        final int N = windows.size();
-        for (int i=0; i<N; i++) {
-            WindowState win = windows.get(i);
-            // If we're animating with a saved surface, we're already visible.
-            // Return true so that the alpha doesn't get cleared.
-            if (!win.mAppFreezing
-                    && (win.mViewVisibility == View.VISIBLE || win.isAnimatingWithSavedSurface()
-                            || (win.mWinAnimator.isAnimationSet()
-                                    && !mService.mAppTransition.isTransitionSet()))
-                    && !win.mDestroying
-                    && win.isDrawnLw()) {
+        final int count = windows.size();
+        for (int i = 0; i < count; i++) {
+            final WindowState win = windows.get(i);
+            if (win.isVisible()) {
                 return true;
             }
         }
         return false;
     }
 
-    boolean isVisibleForUser() {
+    boolean canBeVisibleForCurrentUser() {
         for (int j = windows.size() - 1; j >= 0; j--) {
             final WindowState w = windows.get(j);
             if (!w.isHiddenFromUserLocked()) {
@@ -503,35 +410,7 @@
         boolean wallpaperMightChange = false;
         for (int i = windows.size() - 1; i >= 0; i--) {
             final WindowState win = windows.get(i);
-            // We don't want to clear it out for windows that get replaced, because the
-            // animation depends on the flag to remove the replaced window.
-            //
-            // We also don't clear the mAnimatingExit flag for windows which have the
-            // mRemoveOnExit flag. This indicates an explicit remove request has been issued
-            // by the client. We should let animation proceed and not clear this flag or
-            // they won't eventually be removed by WindowStateAnimator#finishExit.
-            if (!win.mWillReplaceWindow && !win.mRemoveOnExit) {
-                // Clear mAnimating flag together with mAnimatingExit. When animation
-                // changes from exiting to entering, we need to clear this flag until the
-                // new animation gets applied, so that isAnimationStarting() becomes true
-                // until then.
-                // Otherwise applySurfaceChangesTransaction will faill to skip surface
-                // placement for this window during this period, one or more frame will
-                // show up with wrong position or scale.
-                if (win.mAnimatingExit) {
-                    win.mAnimatingExit = false;
-                    wallpaperMightChange = true;
-                }
-                if (win.mWinAnimator.mAnimating) {
-                    win.mWinAnimator.mAnimating = false;
-                    wallpaperMightChange = true;
-                }
-                if (win.mDestroying) {
-                    win.mDestroying = false;
-                    mService.mDestroySurface.remove(win);
-                    wallpaperMightChange = true;
-                }
-            }
+            wallpaperMightChange |= win.clearAnimatingFlags();
         }
         if (wallpaperMightChange) {
             requestUpdateWallpaperIfNeeded();
@@ -551,42 +430,18 @@
      * others so that they are ready to be reused. If set to false (common case), destroy all
      * surfaces that's eligible, if the app is already stopped.
      */
-
     private void destroySurfaces(boolean cleanupOnResume) {
-        final ArrayList<WindowState> allWindows = (ArrayList<WindowState>) windows.clone();
         final DisplayContentList displayList = new DisplayContentList();
-        for (int i = allWindows.size() - 1; i >= 0; i--) {
-            final WindowState win = allWindows.get(i);
+        for (int i = windows.size() - 1; i >= 0; i--) {
+            final WindowState win = windows.get(i);
+            final boolean destroyed = win.destroySurface(cleanupOnResume, mAppStopped);
 
-            if (!(mAppStopped || win.mWindowRemovalAllowed || cleanupOnResume)) {
-                continue;
+            if (destroyed) {
+                final DisplayContent displayContent = win.getDisplayContent();
+                if (displayContent != null && !displayList.contains(displayContent)) {
+                    displayList.add(displayContent);
+                }
             }
-
-            win.mWinAnimator.destroyPreservedSurfaceLocked();
-
-            if (!win.mDestroying) {
-                continue;
-            }
-
-            if (DEBUG_ADD_REMOVE) Slog.e(TAG_WM, "win=" + win
-                    + " destroySurfaces: mAppStopped=" + mAppStopped
-                    + " win.mWindowRemovalAllowed=" + win.mWindowRemovalAllowed
-                    + " win.mRemoveOnExit=" + win.mRemoveOnExit);
-
-            if (!cleanupOnResume || win.mRemoveOnExit) {
-                win.destroyOrSaveSurface();
-            }
-            if (win.mRemoveOnExit) {
-                win.remove();
-            }
-            final DisplayContent displayContent = win.getDisplayContent();
-            if (displayContent != null && !displayList.contains(displayContent)) {
-                displayList.add(displayContent);
-            }
-            if (cleanupOnResume) {
-                win.requestUpdateWallpaperIfNeeded();
-            }
-            win.mDestroying = false;
         }
         for (int i = 0; i < displayList.size(); i++) {
             final DisplayContent displayContent = displayList.get(i);
@@ -646,10 +501,10 @@
         return false;
     }
 
-    void clearVisibleBeforeClientHidden() {
+    void clearWasVisibleBeforeClientHidden() {
         for (int i = windows.size() - 1; i >= 0; i--) {
             final WindowState w = windows.get(i);
-            w.clearVisibleBeforeClientHidden();
+            w.clearWasVisibleBeforeClientHidden();
         }
     }
 
@@ -674,14 +529,7 @@
     void stopUsingSavedSurfaceLocked() {
         for (int i = windows.size() - 1; i >= 0; i--) {
             final WindowState w = windows.get(i);
-            if (w.isAnimatingInvisibleWithSavedSurface()) {
-                if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.d(TAG,
-                        "stopUsingSavedSurfaceLocked: " + w);
-                w.clearAnimatingWithSavedSurface();
-                w.mDestroying = true;
-                w.mWinAnimator.hide("stopUsingSavedSurfaceLocked");
-                mService.mWallpaperControllerLocked.hideWallpapers(w);
-            }
+            w.stopUsingSavedSurface();
         }
         destroySurfaces();
     }
@@ -689,51 +537,40 @@
     void markSavedSurfaceExiting() {
         for (int i = windows.size() - 1; i >= 0; i--) {
             final WindowState w = windows.get(i);
-            if (w.isAnimatingInvisibleWithSavedSurface()) {
-                w.mAnimatingExit = true;
-                w.mWinAnimator.mAnimating = true;
-            }
+            w.markSavedSurfaceExiting();
         }
     }
 
-    void restoreSavedSurfaces() {
+    void restoreSavedSurfaceForInterestingWindows() {
         if (!canRestoreSurfaces()) {
-            clearVisibleBeforeClientHidden();
+            clearWasVisibleBeforeClientHidden();
             return;
         }
-        // Check if we have enough drawn windows to mark allDrawn= true.
-        int numInteresting = 0;
-        int numDrawn = 0;
+
+        // Check if all interesting windows are drawn and we can mark allDrawn=true.
+        int interestingNotDrawn = -1;
+
         for (int i = windows.size() - 1; i >= 0; i--) {
-            WindowState w = windows.get(i);
-            if (w != startingWindow && !w.mAppDied && w.wasVisibleBeforeClientHidden()
-                    && (!mAppAnimator.freezingScreen || !w.mAppFreezing)) {
-                numInteresting++;
-                if (w.hasSavedSurface()) {
-                    w.restoreSavedSurface();
-                }
-                if (w.isDrawnLw()) {
-                    numDrawn++;
-                }
-            }
+            final WindowState w = windows.get(i);
+            interestingNotDrawn = w.restoreSavedSurfaceForInterestingWindow();
         }
 
         if (!allDrawn) {
-            allDrawn = (numInteresting > 0) && (numInteresting == numDrawn);
+            allDrawn = (interestingNotDrawn == 0);
             if (allDrawn) {
                 mService.mH.obtainMessage(NOTIFY_ACTIVITY_DRAWN, token).sendToTarget();
             }
         }
-        clearVisibleBeforeClientHidden();
+        clearWasVisibleBeforeClientHidden();
 
         if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.d(TAG,
-                "restoreSavedSurfaces: " + this + " allDrawn=" + allDrawn
-                + " numInteresting=" + numInteresting + " numDrawn=" + numDrawn);
+                "restoreSavedSurfaceForInterestingWindows: " + this + " allDrawn=" + allDrawn
+                + " interestingNotDrawn=" + interestingNotDrawn);
     }
 
     void destroySavedSurfaces() {
         for (int i = windows.size() - 1; i >= 0; i--) {
-            WindowState win = windows.get(i);
+            final WindowState win = windows.get(i);
             win.destroySavedSurface();
         }
     }
@@ -764,7 +601,7 @@
         }
     }
 
-    void removeAllDeadWindows() {
+    void removeDeadWindows() {
         for (int winNdx = windows.size() - 1; winNdx >= 0;
             // WindowState#removeIfPossible() at bottom of loop may remove multiple entries from
             // windows if the window to be removed has child windows. It also may
@@ -775,9 +612,10 @@
             WindowState win = windows.get(winNdx);
             if (win.mAppDied) {
                 if (DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.w(TAG,
-                        "removeAllDeadWindows: " + win);
+                        "removeDeadWindows: " + win);
                 // Set mDestroying, we don't want any animation or delayed removal here.
                 win.mDestroying = true;
+                // Also removes child windows.
                 win.removeIfPossible();
             }
         }
@@ -785,6 +623,8 @@
 
     boolean hasWindowsAlive() {
         for (int i = windows.size() - 1; i >= 0; i--) {
+            // No need to loop through child windows as the answer should be the same as that of the
+            // parent window.
             if (!windows.get(i).mAppDied) {
                 return true;
             }
@@ -792,42 +632,40 @@
         return false;
     }
 
-    void setReplacingWindows(boolean animate) {
+    void setWillReplaceWindows(boolean animate) {
         if (DEBUG_ADD_REMOVE) Slog.d(TAG_WM,
                 "Marking app token " + this + " with replacing windows.");
 
         for (int i = windows.size() - 1; i >= 0; i--) {
             final WindowState w = windows.get(i);
-            w.setReplacing(animate);
+            w.setWillReplaceWindow(animate);
         }
         if (animate) {
             // Set-up dummy animation so we can start treating windows associated with this
             // token like they are in transition before the new app window is ready for us to
             // run the real transition animation.
             if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM,
-                    "setReplacingWindow() Setting dummy animation on: " + this);
+                    "setWillReplaceWindow() Setting dummy animation on: " + this);
             mAppAnimator.setDummyAnimation();
         }
     }
 
-    void setReplacingChildren() {
+    void setWillReplaceChildWindows() {
         if (DEBUG_ADD_REMOVE) Slog.d(TAG_WM, "Marking app token " + this
                 + " with replacing child windows.");
         for (int i = windows.size() - 1; i >= 0; i--) {
             final WindowState w = windows.get(i);
-            if (w.shouldBeReplacedWithChildren()) {
-                w.setReplacing(false /* animate */);
-            }
+            w.setWillReplaceChildWindows();
         }
     }
 
-    void resetReplacingWindows() {
+    void clearWillReplaceWindows() {
         if (DEBUG_ADD_REMOVE) Slog.d(TAG_WM,
                 "Resetting app token " + this + " of replacing window marks.");
 
         for (int i = windows.size() - 1; i >= 0; i--) {
             final WindowState w = windows.get(i);
-            w.resetReplacing();
+            w.clearWillReplaceWindow();
         }
     }
 
@@ -872,49 +710,31 @@
     void addWindow(WindowState w) {
         super.addWindow(w);
 
+        boolean gotReplacementWindow = false;
         for (int i = windows.size() - 1; i >= 0; i--) {
             final WindowState candidate = windows.get(i);
-            if (candidate.mWillReplaceWindow && candidate.mReplacingWindow == null
-                    && candidate.getWindowTag().toString().equals(w.getWindowTag().toString())) {
+            gotReplacementWindow |= candidate.setReplacementWindowIfNeeded(w);
+        }
 
-                candidate.mReplacingWindow = w;
-                w.mSkipEnterAnimationForSeamlessReplacement = !candidate.mAnimateReplacingWindow;
-                // if we got a replacement window, reset the timeout to give drawing more time
-                mService.scheduleReplacingWindowTimeouts(this);
-            }
+        // if we got a replacement window, reset the timeout to give drawing more time
+        if (gotReplacementWindow) {
+            mService.scheduleWindowReplacementTimeouts(this);
         }
     }
 
     boolean waitingForReplacement() {
         for (int i = windows.size() - 1; i >= 0; i--) {
-            WindowState candidate = windows.get(i);
-            if (candidate.mWillReplaceWindow) {
+            final WindowState candidate = windows.get(i);
+            if (candidate.waitingForReplacement()) {
                 return true;
             }
         }
         return false;
     }
 
-    void clearTimedoutReplacesLocked() {
-        for (int i = windows.size() - 1; i >= 0;
-             // WindowState#remove() at bottom of loop may remove multiple entries from windows if
-             // the window to be removed has child windows. It also may not remove any windows from
-             // windows at all if win is exiting and currently animating away. This ensures that
-             // winNdx is monotonically decreasing and never beyond windows bounds.
-             i = Math.min(i - 1, windows.size() - 1)) {
-            final WindowState candidate = windows.get(i);
-            if (!candidate.mWillReplaceWindow) {
-                continue;
-            }
-            candidate.mWillReplaceWindow = false;
-            if (candidate.mReplacingWindow != null) {
-                candidate.mReplacingWindow.mSkipEnterAnimationForSeamlessReplacement = false;
-            }
-            // Since the window already timed out, remove it immediately now.
-            // Use WindowState#remove() instead of WindowState#removeIfPossible(), as the latter
-            // delays removal on certain conditions, which will leave the stale window in the
-            // stack and marked mWillReplaceWindow=false, so the window will never be removed.
-            candidate.remove();
+    void onWindowReplacementTimeout() {
+        for (int i = windows.size() - 1; i >= 0; --i) {
+            windows.get(i).onWindowReplacementTimeout();
         }
     }
 
@@ -956,14 +776,7 @@
         }
         for (int i = windows.size() - 1; i >= 0; i--) {
             final WindowState win = windows.get(i);
-            if (!win.mHasSurface) {
-                continue;
-            }
-            win.mLayoutNeeded = true;
-            win.setDisplayLayoutNeeded();
-            if (!mService.mResizingWindows.contains(win)) {
-                mService.mResizingWindows.add(win);
-            }
+            win.onUnfreezeBounds();
         }
         mService.mWindowPlacerLocked.performSurfacePlacement();
     }
@@ -1019,52 +832,21 @@
     void setWaitingForDrawnIfResizingChanged() {
         for (int i = windows.size() - 1; i >= 0; --i) {
             final WindowState win = windows.get(i);
-            if (win.isDragResizeChanged()) {
-                mService.mWaitingForDrawn.add(win);
-            }
+            win.setWaitingForDrawnIfResizingChanged();
         }
     }
 
     void resizeWindows() {
-        final ArrayList<WindowState> resizingWindows = mService.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 = 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 (or TYPE_DRAWN_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;
-            }
+            win.addToResizingList();
         }
     }
 
-    void moveWindows() {
+    void setMovedByResize() {
         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;
+            win.setMovedByResize();
         }
     }
 
@@ -1087,6 +869,7 @@
         for (int winNdx = windows.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.
+            // Also removes child windows.
             windows.get(winNdx).removeIfPossible();
             doAnotherLayoutPass = true;
         }
@@ -1097,18 +880,13 @@
 
     void forceWindowsScaleableInTransaction(boolean force) {
         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);
+            windows.get(winNdx).forceWindowsScaleableInTransaction(force);
         }
     }
 
     boolean isAnimating() {
         for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
-            final WindowStateAnimator winAnimator = windows.get(winNdx).mWinAnimator;
-            if (winAnimator.isAnimationSet() || winAnimator.mWin.mAnimatingExit) {
+            if (windows.get(winNdx).isAnimating()) {
                 return true;
             }
         }
@@ -1118,6 +896,7 @@
     void setAppLayoutChanges(int changes, String reason, int displayId) {
         final WindowAnimator windowAnimator = mAppAnimator.mAnimator;
         for (int i = windows.size() - 1; i >= 0; i--) {
+            // Child windows will be on the same display as their parents.
             if (displayId == windows.get(i).getDisplayId()) {
                 windowAnimator.setPendingLayoutChanges(displayId, changes);
                 if (DEBUG_LAYOUT_REPEATS) {
@@ -1132,17 +911,16 @@
     void removeReplacedWindowIfNeeded(WindowState replacement) {
         for (int i = windows.size() - 1; i >= 0; i--) {
             final WindowState win = windows.get(i);
-            if (win.mWillReplaceWindow && win.mReplacingWindow == replacement
-                    && replacement.hasDrawnLw()) {
-                replacement.mSkipEnterAnimationForSeamlessReplacement = false;
-                win.removeReplacedWindow();
+            if (win.removeReplacedWindowIfNeeded(replacement)) {
+                return;
             }
         }
     }
 
     void startFreezingScreen() {
         if (DEBUG_ORIENTATION) logWithStack(TAG, "Set freezing of " + appToken + ": hidden="
-                + hidden + " freezing=" + mAppAnimator.freezingScreen);
+                + hidden + " freezing=" + mAppAnimator.freezingScreen + " hiddenRequested="
+                + hiddenRequested);
         if (!hiddenRequested) {
             if (!mAppAnimator.freezingScreen) {
                 mAppAnimator.freezingScreen = true;
@@ -1157,7 +935,7 @@
             final int count = windows.size();
             for (int i = 0; i < count; i++) {
                 final WindowState w = windows.get(i);
-                w.mAppFreezing = true;
+                w.onStartFreezingScreen();
             }
         }
     }
@@ -1171,18 +949,7 @@
         boolean unfrozeWindows = false;
         for (int i = 0; i < count; i++) {
             final WindowState w = windows.get(i);
-            if (w.mAppFreezing) {
-                w.mAppFreezing = false;
-                if (w.mHasSurface && !w.mOrientationChanging
-                        && mService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) {
-                    if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "set mOrientationChanging of " + w);
-                    w.mOrientationChanging = true;
-                    mService.mWindowPlacerLocked.mOrientationChangeComplete = false;
-                }
-                w.mLastFreezeDuration = 0;
-                unfrozeWindows = true;
-                w.setDisplayLayoutNeeded();
-            }
+            unfrozeWindows |= w.onStopFreezingScreen();
         }
         if (force || unfrozeWindows) {
             if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "No longer freezing: " + this);
@@ -1297,8 +1064,8 @@
         return false;
     }
 
-    int getWindowsCount() {
-        return windows.size();
+    boolean isLastWindow(WindowState win) {
+        return windows.size() == 1 && windows.get(0) == win;
     }
 
     void setAllAppWinAnimators() {
@@ -1307,7 +1074,7 @@
 
         final int windowsCount = windows.size();
         for (int j = 0; j < windowsCount; j++) {
-            allAppWinAnimators.add(windows.get(j).mWinAnimator);
+            windows.get(j).addWinAnimatorToList(allAppWinAnimators);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index cb99461..e9ce0ea 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -202,7 +202,7 @@
 
     @Override
     public void prepareToReplaceWindows(IBinder appToken, boolean childrenOnly) {
-        mService.setReplacingWindows(appToken, childrenOnly);
+        mService.setWillReplaceWindows(appToken, childrenOnly);
     }
 
     public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index b489ad3..3451333 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -17,18 +17,14 @@
 package com.android.server.wm;
 
 import static android.app.ActivityManager.RESIZE_MODE_SYSTEM_SCREEN_ROTATION;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.HOME_STACK_ID;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_CROP_WINDOWS;
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RESIZE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.H.RESIZE_TASK;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 
 import android.app.ActivityManager.StackId;
 import android.content.pm.ActivityInfo;
@@ -44,7 +40,6 @@
 import com.android.server.EventLogTags;
 
 import java.io.PrintWriter;
-import java.util.ArrayList;
 
 class Task implements DimLayer.DimLayerUser {
     static final String TAG = TAG_WITH_CLASS_NAME ? "Task" : TAG_WM;
@@ -311,7 +306,7 @@
         if ((boundsChanged & BOUNDS_CHANGE_SIZE) == BOUNDS_CHANGE_SIZE) {
             resizeWindows();
         } else {
-            moveWindows();
+            setMovedByResize();
         }
         return true;
     }
@@ -545,9 +540,9 @@
         }
     }
 
-    void moveWindows() {
+    void setMovedByResize() {
         for (int i = mAppTokens.size() - 1; i >= 0; --i) {
-            mAppTokens.get(i).moveWindows();
+            mAppTokens.get(i).setMovedByResize();
         }
     }
 
@@ -616,10 +611,6 @@
         return null;
     }
 
-    AppWindowToken getTopAppToken() {
-        return mAppTokens.size() > 0 ? mAppTokens.get(mAppTokens.size() - 1) : null;
-    }
-
     @Override
     public boolean dimFullscreen() {
         return isHomeTask() || isFullscreen();
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 8cf89ec..fc4fdfa 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -91,4 +91,9 @@
         }
         return false;
     }
+
+    /** Returns the top child container or this container if there are no children. */
+    WindowContainer getTop() {
+        return mChildren.isEmpty() ? this : mChildren.peekLast();
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d946ae4..d4750e7 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -431,7 +431,7 @@
      * List of app window tokens that are waiting for replacing windows. If the
      * replacement doesn't come in time the stale windows needs to be disposed of.
      */
-    final ArrayList<AppWindowToken> mReplacingWindowTimeouts = new ArrayList<>();
+    final ArrayList<AppWindowToken> mWindowReplacementTimeouts = new ArrayList<>();
 
     /**
      * The input consumer added to the window manager which consumes input events to windows below
@@ -3646,7 +3646,7 @@
                 // If the app is dead while it was visible, we kept its dead window on screen.
                 // Now that the app is going invisible, we can remove it. It will be restarted
                 // if made visible again.
-                wtoken.removeAllDeadWindows();
+                wtoken.removeDeadWindows();
                 wtoken.setVisibleBeforeClientHidden();
             } else if (visible) {
                 if (!mAppTransition.isTransitionSet() && mAppTransition.isReady()) {
@@ -7748,11 +7748,11 @@
                 break;
                 case WINDOW_REPLACEMENT_TIMEOUT: {
                     synchronized (mWindowMap) {
-                        for (int i = mReplacingWindowTimeouts.size() - 1; i >= 0; i--) {
-                            final AppWindowToken token = mReplacingWindowTimeouts.get(i);
-                            token.clearTimedoutReplacesLocked();
+                        for (int i = mWindowReplacementTimeouts.size() - 1; i >= 0; i--) {
+                            final AppWindowToken token = mWindowReplacementTimeouts.get(i);
+                            token.onWindowReplacementTimeout();
                         }
-                        mReplacingWindowTimeouts.clear();
+                        mWindowReplacementTimeouts.clear();
                     }
                 }
                 case NOTIFY_APP_TRANSITION_STARTING: {
@@ -7877,7 +7877,7 @@
                             && imFocus.mAppToken != null) {
                         // The client has definitely started, so it really should
                         // have a window in this app token.  Let's look for it.
-                        final WindowState w = imFocus.mAppToken.getFirstWindow(imFocus);
+                        final WindowState w = imFocus.mAppToken.getFirstNonStartingWindow();
                         if (w != null) {
                             if (DEBUG_INPUT_METHOD) Slog.i(TAG_WM,
                                     "Switching to real app window: " + w);
@@ -8519,7 +8519,7 @@
                 // If it's a dead window left on screen, and the configuration changed,
                 // there is nothing we can do about it. Remove the window now.
                 if (w.mAppToken != null && w.mAppDied) {
-                    w.mAppToken.removeAllDeadWindows();
+                    w.mAppToken.removeDeadWindows();
                     return;
                 }
 
@@ -10016,16 +10016,15 @@
      * a window.
      * @param token Application token for which the activity will be relaunched.
      */
-    public void setReplacingWindow(IBinder token, boolean animate) {
-        AppWindowToken appWindowToken = null;
+    public void setWillReplaceWindow(IBinder token, boolean animate) {
         synchronized (mWindowMap) {
-            appWindowToken = findAppWindowToken(token);
+            final AppWindowToken appWindowToken = findAppWindowToken(token);
             if (appWindowToken == null || !appWindowToken.isVisible()) {
                 Slog.w(TAG_WM, "Attempted to set replacing window on non-existing app token "
                         + token);
                 return;
             }
-            appWindowToken.setReplacingWindows(animate);
+            appWindowToken.setWillReplaceWindows(animate);
         }
     }
 
@@ -10039,10 +10038,11 @@
      *                     reused rather than replaced).
      *
      */
-    public void setReplacingWindows(IBinder token, boolean childrenOnly) {
-        AppWindowToken appWindowToken = null;
+    // TODO: The s at the end of the method name is the only difference with the name of the method
+    // above. We should combine them or find better names.
+    public void setWillReplaceWindows(IBinder token, boolean childrenOnly) {
         synchronized (mWindowMap) {
-            appWindowToken = findAppWindowToken(token);
+            final AppWindowToken appWindowToken = findAppWindowToken(token);
             if (appWindowToken == null || !appWindowToken.isVisible()) {
                 Slog.w(TAG_WM, "Attempted to set replacing window on non-existing app token "
                         + token);
@@ -10050,12 +10050,12 @@
             }
 
             if (childrenOnly) {
-                appWindowToken.setReplacingChildren();
+                appWindowToken.setWillReplaceChildWindows();
             } else {
-                appWindowToken.setReplacingWindows(false /* animate */);
+                appWindowToken.setWillReplaceWindows(false /* animate */);
             }
 
-            scheduleClearReplacingWindowIfNeeded(token, true /* replacing */);
+            scheduleClearWillReplaceWindows(token, true /* replacing */);
         }
     }
 
@@ -10068,26 +10068,25 @@
      * @param token Application token for the activity whose window might be replaced.
      * @param replacing Whether the window is being replaced or not.
      */
-    public void scheduleClearReplacingWindowIfNeeded(IBinder token, boolean replacing) {
-        AppWindowToken appWindowToken = null;
+    public void scheduleClearWillReplaceWindows(IBinder token, boolean replacing) {
         synchronized (mWindowMap) {
-            appWindowToken = findAppWindowToken(token);
+            final AppWindowToken appWindowToken = findAppWindowToken(token);
             if (appWindowToken == null) {
                 Slog.w(TAG_WM, "Attempted to reset replacing window on non-existing app token "
                         + token);
                 return;
             }
             if (replacing) {
-                scheduleReplacingWindowTimeouts(appWindowToken);
+                scheduleWindowReplacementTimeouts(appWindowToken);
             } else {
-                appWindowToken.resetReplacingWindows();
+                appWindowToken.clearWillReplaceWindows();
             }
         }
     }
 
-    void scheduleReplacingWindowTimeouts(AppWindowToken appWindowToken) {
-        if (!mReplacingWindowTimeouts.contains(appWindowToken)) {
-            mReplacingWindowTimeouts.add(appWindowToken);
+    void scheduleWindowReplacementTimeouts(AppWindowToken appWindowToken) {
+        if (!mWindowReplacementTimeouts.contains(appWindowToken)) {
+            mWindowReplacementTimeouts.add(appWindowToken);
         }
         mH.removeMessages(H.WINDOW_REPLACEMENT_TIMEOUT);
         mH.sendEmptyMessageDelayed(
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index cb8660b..dea06a9 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -99,6 +99,7 @@
 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_ENTER;
 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;
@@ -117,6 +118,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SURFACE_TRACE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -126,6 +128,7 @@
 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.WINDOWS_FREEZING_SCREENS_TIMEOUT;
 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;
@@ -486,7 +489,7 @@
     boolean mAnimateReplacingWindow = false;
     // If not null, the window that will be used to replace the old one. This is being set when
     // the window is added and unset when this window reports its first draw.
-    WindowState mReplacingWindow = null;
+    WindowState mReplacementWindow = null;
     // For the new window in the replacement transition, if we have
     // requested to replace without animation, then we should
     // make sure we also don't apply an enter animation for
@@ -531,6 +534,8 @@
      */
     boolean mSeamlesslyRotated = false;
 
+    private static final Region sEmptyRegion = new Region();
+
     /**
      * Compares to window sub-layers and returns -1 if the first is lesser than the second in terms
      * of z-order and 1 otherwise.
@@ -599,7 +604,7 @@
         }
         mDeathRecipient = deathRecipient;
 
-        if ((mAttrs.type >= FIRST_SUB_WINDOW && mAttrs.type <= LAST_SUB_WINDOW)) {
+        if (mAttrs.type >= FIRST_SUB_WINDOW && mAttrs.type <= LAST_SUB_WINDOW) {
             // The multiplier here is to reserve space for multiple
             // windows in the same type layer.
             mBaseLayer = mPolicy.windowTypeToLayerLw(parentWindow.mAttrs.type)
@@ -1165,6 +1170,26 @@
         }
     }
 
+    // TODO: Sigh...another is visible method...tried to consolidate with other isVisible methods
+    // below, but failed. Need to figure-out a good way to handle this long term...
+    boolean isVisible() {
+        // If we're animating with a saved surface, we're already visible.
+        // Return true so that the alpha doesn't get cleared.
+        if (!mAppFreezing && isDrawnLw()
+                && (mViewVisibility == View.VISIBLE || isAnimatingWithSavedSurface()
+                || (mWinAnimator.isAnimationSet() && !mService.mAppTransition.isTransitionSet()))) {
+            return true;
+        }
+
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowState c = (WindowState) mChildren.get(i);
+            if (c.isVisible()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * Does the minimal check for visibility. Callers generally want to use one of the public
      * methods as they perform additional checks on the app token.
@@ -1402,6 +1427,131 @@
                 && (mAppToken == null || mAppToken.mAppAnimator.animation == null);
     }
 
+    void setMovedByResize() {
+        if (DEBUG_RESIZE) Slog.d(TAG, "setMovedByResize: Moving " + this);
+        mMovedByResize = true;
+
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowState c = (WindowState) mChildren.get(i);
+            c.setMovedByResize();
+        }
+    }
+
+    boolean onAppVisibilityChanged(boolean visible, boolean runningAppAnimation) {
+        boolean changed = false;
+
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowState c = (WindowState) mChildren.get(i);
+            changed |= c.onAppVisibilityChanged(visible, runningAppAnimation);
+        }
+
+        if (mAttrs.type == TYPE_APPLICATION_STARTING) {
+            // 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 && isVisibleNow() && mAppToken.mAppAnimator.isAnimating()) {
+                mAnimatingExit = true;
+                mRemoveOnExit = true;
+                mWindowRemovalAllowed = true;
+            }
+            return changed;
+        }
+
+        if (visible != isVisibleNow()) {
+            if (!runningAppAnimation) {
+                final AccessibilityController accessibilityController =
+                        mService.mAccessibilityController;
+                final int winTransit = visible ? TRANSIT_ENTER : TRANSIT_EXIT;
+                mWinAnimator.applyAnimationLocked(winTransit, visible);
+                //TODO (multidisplay): Magnification is supported only for the default
+                if (accessibilityController != null && getDisplayId() == DEFAULT_DISPLAY) {
+                    accessibilityController.onWindowTransitionLocked(this, winTransit);
+                }
+            }
+            changed = true;
+            setDisplayLayoutNeeded();
+        }
+
+        return changed;
+    }
+
+    boolean onSetAppExiting() {
+        final DisplayContent displayContent = getDisplayContent();
+        boolean changed = false;
+
+        if (isVisibleNow()) {
+            mWinAnimator.applyAnimationLocked(TRANSIT_EXIT, false);
+            //TODO (multidisplay): Magnification is supported only for the default
+            if (mService.mAccessibilityController != null && isDefaultDisplay()) {
+                mService.mAccessibilityController.onWindowTransitionLocked(this, TRANSIT_EXIT);
+            }
+            changed = true;
+            if (displayContent != null) {
+                displayContent.layoutNeeded = true;
+            }
+        }
+
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowState c = (WindowState) mChildren.get(i);
+            changed |= c.onSetAppExiting();
+        }
+
+        return changed;
+    }
+
+    void addToResizingList() {
+        // Some windows won't go through the resizing process, if they don't have a surface, so
+        // destroy all saved surfaces here.
+        destroySavedSurface();
+
+        final ArrayList<WindowState> resizingWindows = mService.mResizingWindows;
+        if (mHasSurface && !resizingWindows.contains(this)) {
+            if (DEBUG_RESIZE) Slog.d(TAG, "resizeWindows: Resizing " + this);
+            resizingWindows.add(this);
+
+            // 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 (!computeDragResizing() && mAttrs.type == TYPE_BASE_APPLICATION &&
+                    !getTask().mStack.getBoundsAnimating() && !isGoneForLayoutLw() &&
+                    !getTask().inPinnedWorkspace()) {
+                setResizedWhileNotDragResizing(true);
+            }
+        }
+        if (isGoneForLayoutLw()) {
+            mResizedWhileGone = true;
+        }
+
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowState c = (WindowState) mChildren.get(i);
+            c.addToResizingList();
+        }
+    }
+
+    void onUnfreezeBounds() {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowState c = (WindowState) mChildren.get(i);
+            c.onUnfreezeBounds();
+        }
+
+        if (!mHasSurface) {
+            return;
+        }
+
+        mLayoutNeeded = true;
+        setDisplayLayoutNeeded();
+        if (!mService.mResizingWindows.contains(this)) {
+            mService.mResizingWindows.add(this);
+        }
+    }
+
     /**
      * Return whether this window has moved. (Only makes
      * sense to call from performLayoutAndPlaceSurfacesLockedInner().)
@@ -1451,6 +1601,34 @@
                 && mAppToken.mTask.mStack.isAdjustedForMinimizedDock();
     }
 
+    void onWindowReplacementTimeout() {
+        if (mWillReplaceWindow) {
+            // Since the window already timed out, remove it immediately now.
+            // Use WindowState#remove() instead of WindowState#removeIfPossible(), as the latter
+            // delays removal on certain conditions, which will leave the stale window in the
+            // stack and marked mWillReplaceWindow=false, so the window will never be removed.
+            //
+            // Also removes child windows.
+            remove();
+        } else {
+            for (int i = mChildren.size() - 1; i >= 0; --i) {
+                final WindowState c = (WindowState) mChildren.get(i);
+                c.onWindowReplacementTimeout();
+            }
+        }
+    }
+
+    void forceWindowsScaleableInTransaction(boolean force) {
+        if (mWinAnimator != null && mWinAnimator.hasSurface()) {
+            mWinAnimator.mSurfaceController.forceScaleableInTransaction(force);
+        }
+
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowState c = (WindowState) mChildren.get(i);
+            c.forceWindowsScaleableInTransaction(force);
+        }
+    }
+
     @Override
     void remove() {
         super.remove();
@@ -1463,6 +1641,11 @@
 
         mRemoved = true;
 
+        mWillReplaceWindow = false;
+        if (mReplacementWindow != null) {
+            mReplacementWindow.mSkipEnterAnimationForSeamlessReplacement = false;
+        }
+
         if (mService.mInputMethodTarget == this) {
             mService.moveInputMethodWindowsIfNeededLocked(false);
         }
@@ -1490,6 +1673,10 @@
     }
 
     void removeIfPossible() {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowState c = (WindowState) mChildren.get(i);
+            c.removeIfPossible(false /*keepVisibleDeadWindow*/);
+        }
         removeIfPossible(false /*keepVisibleDeadWindow*/);
     }
 
@@ -1598,7 +1785,7 @@
             final boolean isAnimating =
                     mWinAnimator.isAnimationSet() && !mWinAnimator.isDummyAnimation();
             final boolean lastWindowIsStartingWindow = startingWindow && mAppToken != null
-                    && mAppToken.getWindowsCount() == 1;
+                    && mAppToken.isLastWindow(this);
             // 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.
@@ -1690,6 +1877,11 @@
      */
     void notifyMovedInStack() {
         mJustMovedInStack = true;
+
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowState c = (WindowState) mChildren.get(i);
+            c.notifyMovedInStack();
+        }
     }
 
     /**
@@ -1706,6 +1898,11 @@
      */
     void resetJustMovedInStack() {
         mJustMovedInStack = false;
+
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            final WindowState c = (WindowState) mChildren.get(i);
+            c.resetJustMovedInStack();
+        }
     }
 
     private final class DeadWindowEventReceiver extends InputEventReceiver {
@@ -1789,6 +1986,23 @@
         return getStack();
     }
 
+    /** Returns true if the replacement window was removed. */
+    boolean removeReplacedWindowIfNeeded(WindowState replacement) {
+        if (mWillReplaceWindow && mReplacementWindow == replacement && replacement.hasDrawnLw()) {
+            replacement.mSkipEnterAnimationForSeamlessReplacement = false;
+            removeReplacedWindow();
+            return true;
+        }
+
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowState c = (WindowState) mChildren.get(i);
+            if (c.removeReplacedWindowIfNeeded(replacement)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     void removeReplacedWindow() {
         if (DEBUG_ADD_REMOVE) Slog.d(TAG, "Removing replaced window: " + this);
         if (isDimming()) {
@@ -1797,12 +2011,31 @@
         mWillReplaceWindow = false;
         mAnimateReplacingWindow = false;
         mReplacingRemoveRequested = false;
-        mReplacingWindow = null;
+        mReplacementWindow = null;
         if (mAnimatingExit || !mAnimateReplacingWindow) {
             remove();
         }
     }
 
+    boolean setReplacementWindowIfNeeded(WindowState replacementCandidate) {
+        boolean replacementSet = false;
+
+        if (mWillReplaceWindow && mReplacementWindow == null
+                && getWindowTag().toString().equals(replacementCandidate.getWindowTag().toString())) {
+
+            mReplacementWindow = replacementCandidate;
+            replacementCandidate.mSkipEnterAnimationForSeamlessReplacement = !mAnimateReplacingWindow;
+            replacementSet = true;
+        }
+
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowState c = (WindowState) mChildren.get(i);
+            replacementSet |= c.setReplacementWindowIfNeeded(replacementCandidate);
+        }
+
+        return replacementSet;
+    }
+
     void setDisplayLayoutNeeded() {
         if (mDisplayContent != null) {
             mDisplayContent.layoutNeeded = true;
@@ -2058,8 +2291,7 @@
                 doAnimation = false;
             }
         }
-        boolean current = doAnimation ? mPolicyVisibilityAfterAnim
-                : mPolicyVisibility;
+        boolean current = doAnimation ? mPolicyVisibilityAfterAnim : mPolicyVisibility;
         if (!current) {
             // Already hiding.
             return false;
@@ -2070,11 +2302,9 @@
                 doAnimation = false;
             }
         }
-        if (doAnimation) {
-            mPolicyVisibilityAfterAnim = false;
-        } else {
+        mPolicyVisibilityAfterAnim = false;
+        if (!doAnimation) {
             if (DEBUG_VISIBILITY) Slog.v(TAG, "Policy visibility false: " + this);
-            mPolicyVisibilityAfterAnim = false;
             mPolicyVisibility = false;
             // Window is no longer visible -- make sure if we were waiting
             // for it to be displayed before enabling the display, that
@@ -2146,24 +2376,141 @@
         return mAnimatingWithSavedSurface;
     }
 
+    boolean isAnimating() {
+        if (mWinAnimator.isAnimationSet() || mAnimatingExit) {
+            return true;
+        }
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowState c = (WindowState) mChildren.get(i);
+            if (c.isAnimating()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     boolean isAnimatingInvisibleWithSavedSurface() {
-        return mAnimatingWithSavedSurface
-                && (mViewVisibility != View.VISIBLE || mWindowRemovalAllowed);
+        if (mAnimatingWithSavedSurface
+                && (mViewVisibility != View.VISIBLE || mWindowRemovalAllowed)) {
+            return true;
+        }
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowState c = (WindowState) mChildren.get(i);
+            if (c.isAnimatingInvisibleWithSavedSurface()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    void stopUsingSavedSurface() {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowState c = (WindowState) mChildren.get(i);
+            c.stopUsingSavedSurface();
+        }
+
+        if (!isAnimatingInvisibleWithSavedSurface()) {
+            return;
+        }
+
+        if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.d(TAG, "stopUsingSavedSurface: " + this);
+        clearAnimatingWithSavedSurface();
+        mDestroying = true;
+        mWinAnimator.hide("stopUsingSavedSurface");
+        mService.mWallpaperControllerLocked.hideWallpapers(this);
+    }
+
+    void markSavedSurfaceExiting() {
+        if (isAnimatingInvisibleWithSavedSurface()) {
+            mAnimatingExit = true;
+            mWinAnimator.mAnimating = true;
+        }
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowState c = (WindowState) mChildren.get(i);
+            c.markSavedSurfaceExiting();
+        }
+    }
+
+    void addWinAnimatorToList(ArrayList<WindowStateAnimator> animators) {
+        animators.add(mWinAnimator);
+
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowState c = (WindowState) mChildren.get(i);
+            c.addWinAnimatorToList(animators);
+        }
+    }
+
+    void sendAppVisibilityToClients(boolean clientHidden) {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowState c = (WindowState) mChildren.get(i);
+            c.sendAppVisibilityToClients(clientHidden);
+        }
+
+        if (mAttrs.type == TYPE_APPLICATION_STARTING && clientHidden) {
+            // Don't hide the starting window.
+            return;
+        }
+
+        try {
+            if (DEBUG_VISIBILITY) Slog.v(TAG,
+                    "Setting visibility of " + this + ": " + (!clientHidden));
+            mClient.dispatchAppVisibility(!clientHidden);
+        } catch (RemoteException e) {
+        }
     }
 
     public void setVisibleBeforeClientHidden() {
         mWasVisibleBeforeClientHidden |=
                 (mViewVisibility == View.VISIBLE || mAnimatingWithSavedSurface);
+
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowState c = (WindowState) mChildren.get(i);
+            c.setVisibleBeforeClientHidden();
+        }
     }
 
-    public void clearVisibleBeforeClientHidden() {
+    public void clearWasVisibleBeforeClientHidden() {
         mWasVisibleBeforeClientHidden = false;
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowState c = (WindowState) mChildren.get(i);
+            c.clearWasVisibleBeforeClientHidden();
+        }
     }
 
     public boolean wasVisibleBeforeClientHidden() {
         return mWasVisibleBeforeClientHidden;
     }
 
+    void onStartFreezingScreen() {
+        mAppFreezing = true;
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowState c = (WindowState) mChildren.get(i);
+            c.onStartFreezingScreen();
+        }
+    }
+
+    boolean onStopFreezingScreen() {
+        boolean unfrozeWindows = false;
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowState c = (WindowState) mChildren.get(i);
+            unfrozeWindows |= c.onStopFreezingScreen();
+        }
+
+        if (!mAppFreezing) {
+            return unfrozeWindows;
+        }
+
+        if (mHasSurface && !mOrientationChanging
+                && mService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) {
+            if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "set mOrientationChanging of " + this);
+            mOrientationChanging = true;
+            mService.mWindowPlacerLocked.mOrientationChangeComplete = false;
+        }
+        mLastFreezeDuration = 0;
+        setDisplayLayoutNeeded();
+        return true;
+    }
+
     private boolean shouldSaveSurface() {
         if (mWinAnimator.mSurfaceController == null) {
             // Don't bother if the surface controller is gone for any reason.
@@ -2212,7 +2559,38 @@
         return mAppToken.shouldSaveSurface();
     }
 
-    static final Region sEmptyRegion = new Region();
+    boolean destroySurface(boolean cleanupOnResume, boolean appStopped) {
+        boolean destroyedSomething = false;
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowState c = (WindowState) mChildren.get(i);
+            destroyedSomething |= c.destroySurface(cleanupOnResume, appStopped);
+        }
+
+        if (appStopped || mWindowRemovalAllowed || cleanupOnResume) {
+
+            mWinAnimator.destroyPreservedSurfaceLocked();
+
+            if (mDestroying) {
+                if (DEBUG_ADD_REMOVE) Slog.e(TAG_WM, "win=" + this
+                        + " destroySurfaces: appStopped=" + appStopped
+                        + " win.mWindowRemovalAllowed=" + mWindowRemovalAllowed
+                        + " win.mRemoveOnExit=" + mRemoveOnExit);
+
+                if (!cleanupOnResume || mRemoveOnExit) {
+                    destroyOrSaveSurface();
+                }
+                if (mRemoveOnExit) {
+                    remove();
+                }
+                if (cleanupOnResume) {
+                    requestUpdateWallpaperIfNeeded();
+                }
+                mDestroying = false;
+                destroyedSomething = true;
+            }
+        }
+        return destroyedSomething;
+    }
 
     void destroyOrSaveSurface() {
         mSurfaceSaved = shouldSaveSurface();
@@ -2243,29 +2621,65 @@
     }
 
     void destroySavedSurface() {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowState c = (WindowState) mChildren.get(i);
+            c.destroySavedSurface();
+        }
+
         if (mSurfaceSaved) {
-            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
-                Slog.v(TAG, "Destroying saved surface: " + this);
-            }
+            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, "Destroying saved surface: " + this);
             mWinAnimator.destroySurfaceLocked();
             mSurfaceSaved = false;
         }
         mWasVisibleBeforeClientHidden = false;
     }
 
-    void restoreSavedSurface() {
-        if (!mSurfaceSaved) {
-            return;
+    /** Returns -1 if there are no interesting windows or number of interesting windows not drawn.*/
+    int restoreSavedSurfaceForInterestingWindow() {
+        int interestingNotDrawn = -1;
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowState c = (WindowState) mChildren.get(i);
+            final int childInterestingNotDrawn = c.restoreSavedSurfaceForInterestingWindow();
+            if (childInterestingNotDrawn != -1) {
+                if (interestingNotDrawn == -1) {
+                    interestingNotDrawn = childInterestingNotDrawn;
+                } else {
+                    interestingNotDrawn += childInterestingNotDrawn;
+                }
+            }
         }
 
-        // Sometimes we save surfaces due to layout invisible
-        // directly after rotation occurs. However this means
-        // the surface was never laid out in the new orientation.
-        // We can only restore to the last rotation we were
-        // laid out as visible in.
+        if (mAttrs.type == TYPE_APPLICATION_STARTING
+                || mAppDied || !wasVisibleBeforeClientHidden()
+                || (mAppToken.mAppAnimator.freezingScreen && mAppFreezing)) {
+            // Window isn't interesting...
+            return interestingNotDrawn;
+        }
+
+        restoreSavedSurface();
+
+        if (!isDrawnLw()) {
+            if (interestingNotDrawn == -1) {
+                interestingNotDrawn = 1;
+            } else {
+                interestingNotDrawn++;
+            }
+        }
+        return interestingNotDrawn;
+    }
+
+    /** Returns true if the saved surface was restored. */
+    boolean restoreSavedSurface() {
+        if (!mSurfaceSaved) {
+            return false;
+        }
+
+        // Sometimes we save surfaces due to layout invisible directly after rotation occurs.
+        // However this means the surface was never laid out in the new orientation.
+        // We can only restore to the last rotation we were laid out as visible in.
         if (mLastVisibleLayoutRotation != mService.mRotation) {
             destroySavedSurface();
-            return;
+            return false;
         }
         mSurfaceSaved = false;
 
@@ -2285,10 +2699,23 @@
             // or resize, mSurfaceSaved flag should have been cleared. So this is a wtf.
             Slog.wtf(TAG, "Failed to restore saved surface: surface gone! " + this);
         }
+
+        return true;
     }
 
     boolean canRestoreSurface() {
-        return mWasVisibleBeforeClientHidden && mSurfaceSaved;
+        if (mWasVisibleBeforeClientHidden && mSurfaceSaved) {
+            return true;
+        }
+
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowState c = (WindowState) mChildren.get(i);
+            if (c.canRestoreSurface()) {
+                return true;
+            }
+        }
+
+        return false;
     }
 
     boolean hasSavedSurface() {
@@ -2601,6 +3028,16 @@
         return mDragResizing != computeDragResizing();
     }
 
+    void setWaitingForDrawnIfResizingChanged() {
+        if (isDragResizeChanged()) {
+            mService.mWaitingForDrawn.add(this);
+        }
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowState c = (WindowState) mChildren.get(i);
+            c.setWaitingForDrawnIfResizingChanged();
+        }
+    }
+
     /**
      * @return Whether we reported a drag resize change to the application or not already.
      */
@@ -2613,6 +3050,10 @@
      */
     void resetDragResizingChangeReported() {
         mDragResizingChangeReported = false;
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowState c = (WindowState) mChildren.get(i);
+            c.resetDragResizingChangeReported();
+        }
     }
 
     /**
@@ -3009,7 +3450,8 @@
     WindowState getBottomChild() {
         // Child windows are z-ordered based on sub-layer using {@link #sWindowSubLayerComparator}
         // and the child with the lowest z-order will be at the head of the list.
-        return (WindowState) mChildren.peekFirst();
+        WindowContainer c = mChildren.peekFirst();
+        return c == null ? null : (WindowState)c;
     }
 
     boolean layoutInParentFrame() {
@@ -3038,7 +3480,12 @@
         return (parent == null) ? false : parent.mHidden;
     }
 
-    void setReplacing(boolean animate) {
+    void setWillReplaceWindow(boolean animate) {
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            final WindowState c = (WindowState) mChildren.get(i);
+            c.setWillReplaceWindow(animate);
+        }
+
         if ((mAttrs.privateFlags & PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH) != 0
                 || mAttrs.type == TYPE_APPLICATION_STARTING) {
             // We don't set replacing on starting windows since they are added by window manager and
@@ -3047,14 +3494,33 @@
         }
 
         mWillReplaceWindow = true;
-        mReplacingWindow = null;
+        mReplacementWindow = null;
         mAnimateReplacingWindow = animate;
     }
 
-    void resetReplacing() {
+    void clearWillReplaceWindow() {
         mWillReplaceWindow = false;
-        mReplacingWindow = null;
+        mReplacementWindow = null;
         mAnimateReplacingWindow = false;
+
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            final WindowState c = (WindowState) mChildren.get(i);
+            c.clearWillReplaceWindow();
+        }
+    }
+
+    boolean waitingForReplacement() {
+        if (mWillReplaceWindow) {
+            return true;
+        }
+
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            final WindowState c = (WindowState) mChildren.get(i);
+            if (c.waitingForReplacement()) {
+                return true;
+            }
+        }
+        return false;
     }
 
     void requestUpdateWallpaperIfNeeded() {
@@ -3063,6 +3529,11 @@
             mDisplayContent.layoutNeeded = true;
             mService.mWindowPlacerLocked.requestTraversal();
         }
+
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            final WindowState c = (WindowState) mChildren.get(i);
+            c.requestUpdateWallpaperIfNeeded();
+        }
     }
 
     float translateToWindowX(float x) {
@@ -3085,7 +3556,7 @@
         final DimLayer.DimLayerUser dimLayerUser = getDimLayerUser();
         if (dimLayerUser != null && mDisplayContent != null) {
             mDisplayContent.mDimLayerController.applyDim(dimLayerUser,
-                    mReplacingWindow.mWinAnimator,
+                    mReplacementWindow.mWinAnimator,
                     (mAttrs.flags & FLAG_DIM_BEHIND) != 0 ? true : false);
         }
     }
@@ -3103,6 +3574,30 @@
                 || mAttrs.type == TYPE_DRAWN_APPLICATION;
     }
 
+    void setWillReplaceChildWindows() {
+        if (shouldBeReplacedWithChildren()) {
+            setWillReplaceWindow(false /* animate */);
+        }
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            final WindowState c = (WindowState) mChildren.get(i);
+            c.setWillReplaceChildWindows();
+        }
+    }
+
+    WindowState getReplacingWindow() {
+        if (mAnimatingExit && mWillReplaceWindow && mAnimateReplacingWindow) {
+            return this;
+        }
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            final WindowState c = (WindowState) mChildren.get(i);
+            final WindowState replacing = c.getReplacingWindow();
+            if (replacing != null) {
+                return replacing;
+            }
+        }
+        return null;
+    }
+
     public int getRotationAnimationHint() {
         if (mAppToken != null) {
             return mAppToken.mRotationAnimationHint;
@@ -3211,13 +3706,30 @@
         return windowInfo;
     }
 
-    void adjustAnimLayer(int adj) {
-        mWinAnimator.mAnimLayer = mLayer + adj;
-        if (DEBUG_LAYERS) Slog.v(TAG_WM, "win=" + this + " anim layer: " + mWinAnimator.mAnimLayer);
+    int getHighestAnimLayer() {
+        int highest = mWinAnimator.mAnimLayer;
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            final WindowState c = (WindowState) mChildren.get(i);
+            final int childLayer = c.getHighestAnimLayer();
+            if (childLayer > highest) {
+                highest = childLayer;
+            }
+        }
+        return highest;
+    }
+
+    int adjustAnimLayer(int adj) {
+        int highestAnimLayer = mWinAnimator.mAnimLayer = mLayer + adj;
+        if (DEBUG_LAYERS || DEBUG_WALLPAPER) Slog.v(TAG_WM,
+                "adjustAnimLayer win=" + this + " anim layer: " + mWinAnimator.mAnimLayer);
         for (int i = mChildren.size() - 1; i >= 0; i--) {
             final WindowState childWindow = (WindowState) mChildren.get(i);
             childWindow.adjustAnimLayer(adj);
+            if (childWindow.mWinAnimator.mAnimLayer > highestAnimLayer) {
+                highestAnimLayer = childWindow.mWinAnimator.mAnimLayer;
+            }
         }
+        return highestAnimLayer;
     }
 
     // TODO: come-up with a better name for this method that represents what it does.
@@ -3280,6 +3792,19 @@
         return interestingPos;
     }
 
+    boolean isWindowAnimationSet() {
+        if (mWinAnimator.isWindowAnimationSet()) {
+            return true;
+        }
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowState c = (WindowState) mChildren.get(i);
+            if (c.isWindowAnimationSet()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     void onExitAnimationDone() {
         if (DEBUG_ANIM) Slog.v(TAG, "onExitAnimationDone in " + this
                 + ": exiting=" + mAnimatingExit + " remove=" + mRemoveOnExit
@@ -3351,11 +3876,54 @@
         mService.mWallpaperControllerLocked.hideWallpapers(this);
     }
 
+    boolean clearAnimatingFlags() {
+        boolean didSomething = false;
+        // We don't want to clear it out for windows that get replaced, because the
+        // animation depends on the flag to remove the replaced window.
+        //
+        // We also don't clear the mAnimatingExit flag for windows which have the
+        // mRemoveOnExit flag. This indicates an explicit remove request has been issued
+        // by the client. We should let animation proceed and not clear this flag or
+        // they won't eventually be removed by WindowStateAnimator#finishExit.
+        if (!mWillReplaceWindow && !mRemoveOnExit) {
+            // Clear mAnimating flag together with mAnimatingExit. When animation
+            // changes from exiting to entering, we need to clear this flag until the
+            // new animation gets applied, so that isAnimationStarting() becomes true
+            // until then.
+            // Otherwise applySurfaceChangesTransaction will fail to skip surface
+            // placement for this window during this period, one or more frame will
+            // show up with wrong position or scale.
+            if (mAnimatingExit) {
+                mAnimatingExit = false;
+                didSomething = true;
+            }
+            if (mWinAnimator.mAnimating) {
+                mWinAnimator.mAnimating = false;
+                didSomething = true;
+            }
+            if (mDestroying) {
+                mDestroying = false;
+                mService.mDestroySurface.remove(this);
+                didSomething = true;
+            }
+        }
+
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            didSomething |= ((WindowState) mChildren.get(i)).clearAnimatingFlags();
+        }
+
+        return didSomething;
+    }
+
     public boolean isRtl() {
         return mMergedConfiguration.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
     }
 
     void hideWallpaperWindow(boolean wasDeferred, String reason) {
+        for (int j = mChildren.size() - 1; j >= 0; --j) {
+            final WindowState c = (WindowState) mChildren.get(j);
+            c.hideWallpaperWindow(wasDeferred, reason);
+        }
         if (!mWinAnimator.mLastHidden || wasDeferred) {
             mWinAnimator.hide(reason);
             dispatchWallpaperVisibility(false);
@@ -3388,6 +3956,19 @@
         }
     }
 
+    boolean hasVisibleNotDrawnWallpaper() {
+        if (mWallpaperVisible && !isDrawnLw()) {
+            return true;
+        }
+        for (int j = mChildren.size() - 1; j >= 0; --j) {
+            final WindowState c = (WindowState) mChildren.get(j);
+            if (c.hasVisibleNotDrawnWallpaper()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     /** Places this window after the input window in the window list. */
     void addWindowToListAfter(WindowState pos) {
         final WindowList windows = pos.getWindowList();
@@ -3441,4 +4022,57 @@
         windows.add(i, this);
         mService.mWindowsChanged = true;
     }
+
+    void updateReportedVisibility(UpdateReportedVisibilityResults results) {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowState c = (WindowState) mChildren.get(i);
+            c.updateReportedVisibility(results);
+        }
+
+        if (mAppFreezing || mViewVisibility != View.VISIBLE
+                || mAttrs.type == TYPE_APPLICATION_STARTING
+                || mDestroying) {
+            return;
+        }
+        if (DEBUG_VISIBILITY) {
+            Slog.v(TAG, "Win " + this + ": isDrawn=" + isDrawnLw()
+                    + ", isAnimationSet=" + mWinAnimator.isAnimationSet());
+            if (!isDrawnLw()) {
+                Slog.v(TAG, "Not displayed: s=" + mWinAnimator.mSurfaceController
+                        + " pv=" + mPolicyVisibility
+                        + " mDrawState=" + mWinAnimator.mDrawState
+                        + " ph=" + isParentWindowHidden()
+                        + " th=" + (mAppToken != null ? mAppToken.hiddenRequested : false)
+                        + " a=" + mWinAnimator.mAnimating);
+            }
+        }
+
+        results.numInteresting++;
+        if (isDrawnLw()) {
+            results.numDrawn++;
+            if (!mWinAnimator.isAnimationSet()) {
+                results.numVisible++;
+            }
+            results.nowGone = false;
+        } else if (mWinAnimator.isAnimationSet()) {
+            results.nowGone = false;
+        }
+    }
+
+    // TODO: Hack to work around the number of states AppWindowToken needs to access without having
+    // access to its windows children. Need to investigate re-writing
+    // {@link AppWindowToken#updateReportedVisibilityLocked} so this can be removed.
+    static final class UpdateReportedVisibilityResults {
+        int numInteresting;
+        int numVisible;
+        int numDrawn;
+        boolean nowGone = true;
+
+        void reset() {
+            numInteresting = 0;
+            numVisible = 0;
+            numDrawn = 0;
+            nowGone = true;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index dfee8de..b9956c8 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -597,10 +597,9 @@
 
     WindowSurfaceController createSurfaceLocked() {
         final WindowState w = mWin;
-        if (w.hasSavedSurface()) {
+        if (w.restoreSavedSurface()) {
             if (DEBUG_ANIM) Slog.i(TAG,
                     "createSurface: " + this + ": called when we had a saved surface");
-            w.restoreSavedSurface();
             return mSurfaceController;
         }
 
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 4c9211a..d0c73d3 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -1352,7 +1352,7 @@
                 }
 
                 final boolean drawnBeforeRestoring = wtoken.allDrawn;
-                wtoken.restoreSavedSurfaces();
+                wtoken.restoreSavedSurfaceForInterestingWindows();
 
                 if (!wtoken.allDrawn && !wtoken.startingDisplayed && !wtoken.startingMoved) {
                     return false;
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index b7bc672..553b056 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -28,14 +28,13 @@
 import java.util.ArrayList;
 
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_SCRIM;
-import static android.view.WindowManagerPolicy.TRANSIT_EXIT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -96,14 +95,8 @@
     }
 
     void removeAllWindows() {
-        for (int winNdx = windows.size() - 1; winNdx >= 0;
-                // WindowState#removeIfPossible() at bottom of loop may remove multiple entries from
-                // allAppWindows if the window to be removed has child windows. It also may not
-                // remove any windows from allAppWindows at all if win is exiting and currently
-                // animating away. This ensures that winNdx is monotonically decreasing and never
-                // beyond allAppWindows bounds.
-                winNdx = Math.min(winNdx - 1, windows.size() - 1)) {
-            WindowState win = windows.get(winNdx);
+        for (int i = windows.size() - 1; i >= 0; --i) {
+            final WindowState win = windows.get(i);
             if (DEBUG_WINDOW_MOVEMENT) Slog.w(TAG_WM, "removeAllWindows: removing win=" + win);
             win.removeIfPossible();
         }
@@ -115,30 +108,20 @@
             return;
         }
 
-        boolean delayed = false;
         final int count = windows.size();
         boolean changed = false;
+        boolean delayed = false;
         DisplayContent displayContent = null;
 
         for (int i = 0; i < count; i++) {
             final WindowState win = windows.get(i);
-            displayContent = win.getDisplayContent();
-
             if (win.mWinAnimator.isAnimationSet()) {
                 delayed = true;
+                // TODO: This is technically wrong as a token can have windows on multi-displays
+                // currently. That will change moving forward though.
+                displayContent = win.getDisplayContent();
             }
-
-            if (win.isVisibleNow()) {
-                win.mWinAnimator.applyAnimationLocked(TRANSIT_EXIT, false);
-                //TODO (multidisplay): Magnification is supported only for the default
-                if (mService.mAccessibilityController != null && win.isDefaultDisplay()) {
-                    mService.mAccessibilityController.onWindowTransitionLocked(win, TRANSIT_EXIT);
-                }
-                changed = true;
-                if (displayContent != null) {
-                    displayContent.layoutNeeded = true;
-                }
-            }
+            changed |= win.onSetAppExiting();
         }
 
         hidden = true;
@@ -157,12 +140,9 @@
         int highestAnimLayer = -1;
         for (int j = windows.size() - 1; j >= 0; j--) {
             final WindowState w = windows.get(j);
-            w.adjustAnimLayer(adj);
-            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;
+            final int winHighestAnimLayer = w.adjustAnimLayer(adj);
+            if (winHighestAnimLayer > highestAnimLayer) {
+                highestAnimLayer = winHighestAnimLayer;
             }
             if (w == mService.mInputMethodTarget && !mService.mInputMethodTargetWaitingAnim) {
                 mService.mLayersController.setInputMethodAnimLayerAdjustment(adj);
@@ -175,7 +155,7 @@
         if (windows.isEmpty()) {
             return null;
         }
-        return windows.get(windows.size() - 1);
+        return (WindowState) windows.get(windows.size() - 1).getTop();
     }
 
     /**
@@ -198,18 +178,22 @@
      * @param displayContent The display we are interested in.
      * @return List of windows from token that are on displayContent.
      */
-    protected WindowList getTokenWindowsOnDisplay(DisplayContent displayContent) {
+    private WindowList getTokenWindowsOnDisplay(DisplayContent displayContent) {
         final WindowList windowList = new WindowList();
-        final int count = windows.size();
+        final WindowList displayWindows = displayContent.getWindowList();
+        final int count = displayWindows.size();
         for (int i = 0; i < count; i++) {
-            final WindowState win = windows.get(i);
-            if (win.getDisplayContent() == displayContent) {
+            final WindowState win = displayWindows.get(i);
+            if (win.mToken == this) {
                 windowList.add(win);
             }
         }
         return windowList;
     }
 
+    // TODO: Now that we are no longer adding child windows to token directly, the rest of the code
+    // in this method doesn't really belong here, but is it difficult to move at the moment. Need to
+    // re-evaluate when figuring-out what to do about display window list.
     private void addChildWindow(final WindowState win) {
         final DisplayContent displayContent = win.getDisplayContent();
         if (displayContent == null) {
@@ -235,30 +219,18 @@
             if (sublayer < 0) {
                 // For negative sublayers, we go below all windows in the same sublayer.
                 if (wSublayer >= sublayer) {
-                    if (!windows.contains(win)) {
-                        if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "Adding " + win + " to " + this);
-                        windows.add(i, win);
-                    }
                     win.addWindowToListBefore(wSublayer >= 0 ? parentWindow : w);
                     break;
                 }
             } else {
                 // For positive sublayers, we go above all windows in the same sublayer.
                 if (wSublayer > sublayer) {
-                    if (!windows.contains(win)) {
-                        if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "Adding " + win + " to " + this);
-                        windows.add(i, win);
-                    }
                     win.addWindowToListBefore(w);
                     break;
                 }
             }
         }
         if (i >= wCount) {
-            if (!windows.contains(win)) {
-                if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "Adding " + win + " to " + this);
-                windows.add(win);
-            }
             if (sublayer < 0) {
                 win.addWindowToListBefore(parentWindow);
             } else {
@@ -364,7 +336,7 @@
         for ( ; taskNdx >= 0; --taskNdx) {
             AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
             for ( ; tokenNdx >= 0; --tokenNdx) {
-                final AppWindowToken t = tokens.get(tokenNdx);
+                final WindowToken t = tokens.get(tokenNdx);
                 tokenWindowList = t.getTokenWindowsOnDisplay(displayContent);
                 final int NW = tokenWindowList.size();
                 if (NW > 0) {
@@ -455,16 +427,6 @@
         final int count = windows.size();
         for (int i = 0; i < count; i++) {
             final WindowState win = windows.get(i);
-            if (win.isChildWindow()) {
-                // The WindowState.reAddWindow below already takes care of re-adding the
-                // child windows for any parent window in this token. This is a side effect of
-                // ensuring child windows are in the same WindowToken as their parent window.
-                //
-                // TODO: Can be removed once WindowToken no longer contains child windows. i.e it is
-                // using WindowContainer which uses the hierarchy to access child windows through
-                // their parent window.
-                continue;
-            }
             final DisplayContent winDisplayContent = win.getDisplayContent();
             if (winDisplayContent == displayContent || winDisplayContent == null) {
                 win.mDisplayContent = displayContent;
@@ -489,11 +451,13 @@
         return -1;
     }
 
-    /** Return the first window in the token window list that isn't the exclude window or null. */
-    WindowState getFirstWindow(WindowState exclude) {
-        for (int i = 0; i < windows.size(); i++) {
-            WindowState w = windows.get(i);
-            if (w != exclude) {
+    /** Return the first window in the token window list that isn't a starting window or null. */
+    WindowState getFirstNonStartingWindow() {
+        final int count = windows.size();
+        // We only care about parent windows so no need to loop through child windows.
+        for (int i = 0; i < count; i++) {
+            final WindowState w = windows.get(i);
+            if (w.mAttrs.type != TYPE_APPLICATION_STARTING) {
                 return w;
             }
         }
@@ -513,8 +477,9 @@
     WindowState getReplacingWindow() {
         for (int i = windows.size() - 1; i >= 0; i--) {
             final WindowState win = windows.get(i);
-            if (win.mAnimatingExit && win.mWillReplaceWindow && win.mAnimateReplacingWindow) {
-                return win;
+            final WindowState replacing = win.getReplacingWindow();
+            if (replacing != null) {
+                return replacing;
             }
         }
         return null;
@@ -665,7 +630,7 @@
     boolean hasVisibleNotDrawnWallpaper() {
         for (int j = windows.size() - 1; j >= 0; --j) {
             final WindowState wallpaper = windows.get(j);
-            if (wallpaper.mWallpaperVisible && !wallpaper.isDrawnLw()) {
+            if (wallpaper.hasVisibleNotDrawnWallpaper()) {
                 return true;
             }
         }
@@ -673,14 +638,15 @@
     }
 
     int getHighestAnimLayer() {
-        int layer = -1;
+        int highest = -1;
         for (int j = 0; j < windows.size(); j++) {
-            final WindowState win = windows.get(j);
-            if (win.mWinAnimator.mAnimLayer > layer) {
-                layer = win.mWinAnimator.mAnimLayer;
+            final WindowState w = windows.get(j);
+            final int wLayer = w.getHighestAnimLayer();
+            if (wLayer > highest) {
+                highest = wLayer;
             }
         }
-        return layer;
+        return highest;
     }
 
     AppWindowToken asAppWindowToken() {