Merge "Fix a flicker when window is removed during entering animation" into nyc-dev
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index e490a40..abbb5f4 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -88,6 +88,11 @@
     // case do not clear allDrawn until the animation completes.
     boolean deferClearAllDrawn;
 
+    // These are to track the app's real drawing status if there were no saved surfaces.
+    boolean allDrawnExcludingSaved;
+    int numInterestingWindowsExcludingSaved;
+    int numDrawnWindowsExclusingSaved;
+
     // Is this window's surface needed?  This is almost like hidden, except
     // it will sometimes be true a little earlier: when the token has
     // been shown, but is still waiting for its app transition to execute
@@ -411,6 +416,39 @@
         }
     }
 
+    /**
+     * Whether the app has some window that is invisible in layout, but
+     * animating with saved surface.
+     */
+    boolean isAnimatingInvisibleWithSavedSurface() {
+        for (int i = allAppWindows.size() - 1; i >= 0; i--) {
+            final WindowState w = allAppWindows.get(i);
+            if (w.isAnimatingInvisibleWithSavedSurface()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Hide all window surfaces that's still invisible in layout but animating
+     * with a saved surface, and mark them destroying.
+     */
+    void stopUsingSavedSurfaceLocked() {
+        for (int i = allAppWindows.size() - 1; i >= 0; i--) {
+            final WindowState w = allAppWindows.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");
+                w.mWinAnimator.mWallpaperControllerLocked.hideWallpapers(w);
+            }
+        }
+        destroySurfaces();
+    }
+
     void restoreSavedSurfaces() {
         if (!canRestoreSurfaces()) {
             clearVisibleBeforeClientHidden();
@@ -456,6 +494,7 @@
     void clearAllDrawn() {
         allDrawn = false;
         deferClearAllDrawn = false;
+        allDrawnExcludingSaved = false;
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index eae7838..be060d2 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -788,6 +788,7 @@
             removeReplacedWindowsLocked();
         }
 
+        mService.stopUsingSavedSurfaceLocked();
         mService.destroyPreservedSurfaceLocked();
         mService.mWindowPlacerLocked.destroyPendingSurfaces();
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 7bdd1b3..8fa9efb 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -415,6 +415,13 @@
     final ArrayList<AppWindowToken> mFinishedStarting = new ArrayList<>();
 
     /**
+     * List of window tokens that have finished drawing their own windows and
+     * no longer need to show any saved surfaces. Windows that's still showing
+     * saved surfaces will be cleaned up after next animation pass.
+     */
+    final ArrayList<AppWindowToken> mFinishedEarlyAnim = new ArrayList<>();
+
+    /**
      * 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.
      */
@@ -2330,6 +2337,23 @@
                 Binder.restoreCallingIdentity(origId);
                 return;
             }
+
+            if (win.isAnimatingWithSavedSurface() && !appToken.allDrawnExcludingSaved) {
+                // We started enter animation early with a saved surface, now the app asks to remove
+                // this window. If we remove it now and the app is not yet drawn, we'll show a
+                // flicker. Delay the removal now until it's really drawn.
+                if (DEBUG_ADD_REMOVE) {
+                    Slog.d(TAG_WM, "removeWindowLocked: delay removal of " + win
+                            + " due to early animation");
+                }
+                // Do not set mAnimatingExit to true here, it will cause the surface to be hidden
+                // immediately after the enter animation is done. If the app is not yet drawn then
+                // it will show up as a flicker.
+                win.mRemoveOnExit = true;
+                win.mWindowRemovalAllowed = true;
+                Binder.restoreCallingIdentity(origId);
+                return;
+            }
             // If we are not currently running the exit animation, we need to see about starting one
             wasVisible = win.isWinVisibleLw();
 
@@ -8560,6 +8584,15 @@
         }
         mDestroyPreservedSurface.clear();
     }
+
+    void stopUsingSavedSurfaceLocked() {
+        for (int i = mFinishedEarlyAnim.size() - 1; i >= 0 ; i--) {
+            final AppWindowToken wtoken = mFinishedEarlyAnim.get(i);
+            wtoken.stopUsingSavedSurfaceLocked();
+        }
+        mFinishedEarlyAnim.clear();
+    }
+
     // -------------------------------------------------------------
     // IWindowManager API
     // -------------------------------------------------------------
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index b66de89..c9d945a 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -437,7 +437,9 @@
     // used to start an entering animation earlier.
     private boolean mSurfaceSaved = false;
 
-    // Whether we're performing an entering animation with a saved surface.
+    // Whether we're performing an entering animation with a saved surface. This flag is
+    // true during the time we're showing a window with a previously saved surface. It's
+    // cleared when surface is destroyed, saved, or re-drawn by the app.
     private boolean mAnimatingWithSavedSurface;
 
     // Whether the window was visible when we set the app to invisible last time. WM uses
@@ -1256,6 +1258,32 @@
     }
 
     /**
+     * Whether this window's drawn state might affect the drawn states of the app token.
+     *
+     * @param visibleOnly Whether we should consider only the windows that's currently
+     *                    visible in layout. If true, windows that has not relayout to VISIBLE
+     *                    would always return false.
+     *
+     * @return true if the window should be considered while evaluating allDrawn flags.
+     */
+    boolean mightAffectAllDrawn(boolean visibleOnly) {
+        final boolean isViewVisible = (mViewVisibility == View.VISIBLE)
+                && (mAppToken == null || !mAppToken.clientHidden);
+        return (isOnScreenIgnoringKeyguard() && (!visibleOnly || isViewVisible)
+                || mWinAnimator.mAttrType == TYPE_BASE_APPLICATION)
+                && !mAnimatingExit && !mDestroying;
+    }
+
+    /**
+     * Whether this window is "interesting" when evaluating allDrawn. If it's interesting,
+     * it must be drawn before allDrawn can become true.
+     */
+    boolean isInteresting() {
+        return mAppToken != null && !mAppDied
+                && (!mAppToken.mAppAnimator.freezingScreen || !mAppFreezing);
+    }
+
+    /**
      * Like isOnScreen(), but we don't return true if the window is part
      * of a transition that has not yet been started.
      */
@@ -1960,6 +1988,11 @@
         return mAnimatingWithSavedSurface;
     }
 
+    boolean isAnimatingInvisibleWithSavedSurface() {
+        return mAnimatingWithSavedSurface
+                && (mViewVisibility != View.VISIBLE || mWindowRemovalAllowed);
+    }
+
     public void setVisibleBeforeClientHidden() {
         mWasVisibleBeforeClientHidden |=
                 (mViewVisibility == View.VISIBLE || mAnimatingWithSavedSurface);
@@ -2042,6 +2075,7 @@
             if (mWinAnimator.mSurfaceController != null) {
                 mWinAnimator.mSurfaceController.disconnectInTransaction();
             }
+            mAnimatingWithSavedSurface = false;
         } else {
             mWinAnimator.destroySurfaceLocked();
         }
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index e20e245..7e9993d 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -792,15 +792,16 @@
                             + " isOnScreen=" + w.isOnScreen() + " allDrawn=" + atoken.allDrawn
                             + " freezingScreen=" + atoken.mAppAnimator.freezingScreen);
                 }
-                if (atoken != null && (!atoken.allDrawn || atoken.mAppAnimator.freezingScreen)) {
+                if (atoken != null && (!atoken.allDrawn || !atoken.allDrawnExcludingSaved
+                        || atoken.mAppAnimator.freezingScreen)) {
                     if (atoken.lastTransactionSequence != mService.mTransactionSequence) {
                         atoken.lastTransactionSequence = mService.mTransactionSequence;
                         atoken.numInterestingWindows = atoken.numDrawnWindows = 0;
+                        atoken.numInterestingWindowsExcludingSaved = 0;
+                        atoken.numDrawnWindowsExclusingSaved = 0;
                         atoken.startingDisplayed = false;
                     }
-                    if ((w.isOnScreenIgnoringKeyguard()
-                            || winAnimator.mAttrType == TYPE_BASE_APPLICATION)
-                            && !w.mAnimatingExit && !w.mDestroying) {
+                    if (!atoken.allDrawn && w.mightAffectAllDrawn(false /* visibleOnly */)) {
                         if (DEBUG_VISIBILITY || DEBUG_ORIENTATION) {
                             Slog.v(TAG, "Eval win " + w + ": isDrawn="
                                     + w.isDrawnLw()
@@ -816,13 +817,14 @@
                             }
                         }
                         if (w != atoken.startingWindow) {
-                            if (!w.mAppDied &&
-                                    (!atoken.mAppAnimator.freezingScreen || !w.mAppFreezing)) {
+                            if (w.isInteresting()) {
                                 atoken.numInterestingWindows++;
                                 if (w.isDrawnLw()) {
                                     atoken.numDrawnWindows++;
                                     if (DEBUG_VISIBILITY || DEBUG_ORIENTATION)
                                         Slog.v(TAG, "tokenMayBeDrawn: " + atoken
+                                                + " w=" + w + " numInteresting="
+                                                + atoken.numInterestingWindows
                                                 + " freezingScreen="
                                                 + atoken.mAppAnimator.freezingScreen
                                                 + " mAppFreezing=" + w.mAppFreezing);
@@ -834,6 +836,23 @@
                             atoken.startingDisplayed = true;
                         }
                     }
+                    if (!atoken.allDrawnExcludingSaved
+                            && w.mightAffectAllDrawn(true /* visibleOnly */)) {
+                        if (w != atoken.startingWindow && w.isInteresting()) {
+                            atoken.numInterestingWindowsExcludingSaved++;
+                            if (w.isDrawnLw() && !w.isAnimatingWithSavedSurface()) {
+                                atoken.numDrawnWindowsExclusingSaved++;
+                                if (DEBUG_VISIBILITY || DEBUG_ORIENTATION)
+                                    Slog.v(TAG, "tokenMayBeDrawnExcludingSaved: " + atoken
+                                            + " w=" + w + " numInteresting="
+                                            + atoken.numInterestingWindowsExcludingSaved
+                                            + " freezingScreen="
+                                            + atoken.mAppAnimator.freezingScreen
+                                            + " mAppFreezing=" + w.mAppFreezing);
+                                updateAllDrawn = true;
+                            }
+                        }
+                    }
                 }
 
                 if (isDefaultDisplay && someoneLosingFocus && w == mService.mCurrentFocus
@@ -1539,6 +1558,22 @@
                                     wtoken.token).sendToTarget();
                         }
                     }
+                    if (!wtoken.allDrawnExcludingSaved) {
+                        int numInteresting = wtoken.numInterestingWindowsExcludingSaved;
+                        if (numInteresting > 0
+                                && wtoken.numDrawnWindowsExclusingSaved >= numInteresting) {
+                            if (DEBUG_VISIBILITY)
+                                Slog.v(TAG, "allDrawnExcludingSaved: " + wtoken
+                                    + " interesting=" + numInteresting
+                                    + " drawn=" + wtoken.numDrawnWindowsExclusingSaved);
+                            wtoken.allDrawnExcludingSaved = true;
+                            displayContent.layoutNeeded = true;
+                            if (wtoken.isAnimatingInvisibleWithSavedSurface()
+                                    && !mService.mFinishedEarlyAnim.contains(wtoken)) {
+                                mService.mFinishedEarlyAnim.add(wtoken);
+                            }
+                        }
+                    }
                 }
             }
         }