Fix blink when docking a window.

We need to be very precise about removing the old window when the new
one is shown. The right moment is immediately after the first frame
of the new window entrance animation gets commited. This requires
more infrastructure and flags, rather than depending on the old ones
and hoping that they will match our needs.

Bug: 25075151

Change-Id: I0fdbf38d0915ded3300d75fc7268e619b574bcf5
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index f048da0..425ff9b 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -133,6 +133,9 @@
     // 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;
+    // Whether the new window has replaced the old one, so the old one can be removed without
+    // blinking.
+    boolean mHasReplacedWindow;
 
     AppWindowToken(WindowManagerService _service, IApplicationToken _token,
             boolean _voiceInteraction) {
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 228da0f..38bd71d 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -18,29 +18,35 @@
 
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
-
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+import static com.android.server.wm.WindowManagerService.DEBUG_ANIM;
+import static com.android.server.wm.WindowManagerService.DEBUG_FOCUS_LIGHT;
 import static com.android.server.wm.WindowManagerService.DEBUG_KEYGUARD;
-import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION;
-import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE;
+import static com.android.server.wm.WindowManagerService.DEBUG_LAYOUT_REPEATS;
+import static com.android.server.wm.WindowManagerService.DEBUG_ORIENTATION;
+import static com.android.server.wm.WindowManagerService.DEBUG_VISIBILITY;
+import static com.android.server.wm.WindowManagerService.DEBUG_WALLPAPER;
+import static com.android.server.wm.WindowManagerService.DEBUG_WINDOW_TRACE;
+import static com.android.server.wm.WindowManagerService.SHOW_TRANSACTIONS;
 import static com.android.server.wm.WindowSurfacePlacer.SET_FORCE_HIDING_CHANGED;
 import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
+import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION;
 import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING;
+import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE;
 
 import android.content.Context;
-import android.os.RemoteException;
 import android.os.Trace;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TimeUtils;
+import android.view.Choreographer;
 import android.view.Display;
 import android.view.SurfaceControl;
 import android.view.WindowManagerPolicy;
 import android.view.animation.AlphaAnimation;
 import android.view.animation.Animation;
-import android.view.Choreographer;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -101,6 +107,10 @@
     static final int KEYGUARD_ANIMATING_OUT = 2;
     int mForceHiding = KEYGUARD_NOT_SHOWN;
 
+    // When set to true the animator will go over all windows after an animation frame is posted and
+    // check if some got replaced and can be removed.
+    private boolean mRemoveReplacedWindows = false;
+
     private String forceHidingToString() {
         switch (mForceHiding) {
             case KEYGUARD_NOT_SHOWN:    return "KEYGUARD_NOT_SHOWN";
@@ -164,7 +174,7 @@
                         setAppLayoutChanges(appAnimator,
                                 WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER,
                                 "appToken " + appAnimator.mAppToken + " done", displayId);
-                        if (WindowManagerService.DEBUG_ANIM) Slog.v(TAG,
+                        if (DEBUG_ANIM) Slog.v(TAG,
                                 "updateWindowsApps...: done animating " + appAnimator.mAppToken);
                     }
                 }
@@ -182,7 +192,7 @@
                     setAppLayoutChanges(appAnimator,
                             WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER,
                             "exiting appToken " + appAnimator.mAppToken + " done", displayId);
-                    if (WindowManagerService.DEBUG_ANIM) Slog.v(TAG,
+                    if (DEBUG_ANIM) Slog.v(TAG,
                             "updateWindowsApps...: done animating exiting "
                                     + appAnimator.mAppToken);
                 }
@@ -274,7 +284,7 @@
                 winAnimator.mWasAnimating = nowAnimating;
                 mAnimating |= nowAnimating;
 
-                if (WindowManagerService.DEBUG_WALLPAPER) {
+                if (DEBUG_WALLPAPER) {
                     Slog.v(TAG, win + ": wasAnimating=" + wasAnimating +
                             ", nowAnimating=" + nowAnimating);
                 }
@@ -284,7 +294,7 @@
                     mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE;
                     setPendingLayoutChanges(Display.DEFAULT_DISPLAY,
                             WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
-                    if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
+                    if (DEBUG_LAYOUT_REPEATS) {
                         mWindowPlacerLocked.debugLayoutRepeats(
                                 "updateWindowsAndWallpaperLocked 2",
                                 getPendingLayoutChanges(Display.DEFAULT_DISPLAY));
@@ -293,13 +303,13 @@
 
                 if (mPolicy.isForceHiding(win.mAttrs)) {
                     if (!wasAnimating && nowAnimating) {
-                        if (DEBUG_KEYGUARD || WindowManagerService.DEBUG_ANIM ||
-                                WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
+                        if (DEBUG_KEYGUARD || DEBUG_ANIM ||
+                                DEBUG_VISIBILITY) Slog.v(TAG,
                                 "Animation started that could impact force hide: " + win);
                         mBulkUpdateParams |= SET_FORCE_HIDING_CHANGED;
                         setPendingLayoutChanges(displayId,
                                 WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
-                        if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
+                        if (DEBUG_LAYOUT_REPEATS) {
                             mWindowPlacerLocked.debugLayoutRepeats(
                                     "updateWindowsAndWallpaperLocked 3",
                                     getPendingLayoutChanges(displayId));
@@ -318,7 +328,7 @@
                             mForceHiding = win.isDrawnLw() ? KEYGUARD_SHOWN : KEYGUARD_NOT_SHOWN;
                         }
                     }
-                    if (DEBUG_KEYGUARD || WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
+                    if (DEBUG_KEYGUARD || DEBUG_VISIBILITY) Slog.v(TAG,
                             "Force hide " + forceHidingToString()
                             + " hasSurface=" + win.mHasSurface
                             + " policyVis=" + win.mPolicyVisibility
@@ -333,7 +343,7 @@
                             // Was already hidden
                             continue;
                         }
-                        if (DEBUG_KEYGUARD || WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
+                        if (DEBUG_KEYGUARD || DEBUG_VISIBILITY) Slog.v(TAG,
                                 "Now policy hidden: " + win);
                     } else {
                         boolean applyExistingExitAnimation = mPostKeyguardExitAnimation != null
@@ -355,7 +365,7 @@
                             win.hideLw(false, false);
                             continue;
                         }
-                        if (DEBUG_KEYGUARD || WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
+                        if (DEBUG_KEYGUARD || DEBUG_VISIBILITY) Slog.v(TAG,
                                 "Now policy shown: " + win);
                         if ((mBulkUpdateParams & SET_FORCE_HIDING_CHANGED) != 0
                                 && win.mAttachedWindow == null) {
@@ -385,7 +395,7 @@
                             // We are showing on top of the current
                             // focus, so re-evaluate focus to make
                             // sure it is correct.
-                            if (WindowManagerService.DEBUG_FOCUS_LIGHT) Slog.v(TAG,
+                            if (DEBUG_FOCUS_LIGHT) Slog.v(TAG,
                                     "updateWindowsLocked: setting mFocusMayChange true");
                             mService.mFocusMayChange = true;
                         }
@@ -394,7 +404,7 @@
                         mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE;
                         setPendingLayoutChanges(Display.DEFAULT_DISPLAY,
                                 WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
-                        if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
+                        if (DEBUG_LAYOUT_REPEATS) {
                             mWindowPlacerLocked.debugLayoutRepeats(
                                     "updateWindowsAndWallpaperLocked 4",
                                     getPendingLayoutChanges(Display.DEFAULT_DISPLAY));
@@ -419,7 +429,7 @@
                     if (winAnimator.performShowLocked()) {
                         setPendingLayoutChanges(displayId,
                                 WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM);
-                        if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
+                        if (DEBUG_LAYOUT_REPEATS) {
                             mWindowPlacerLocked.debugLayoutRepeats(
                                     "updateWindowsAndWallpaperLocked 5",
                                     getPendingLayoutChanges(displayId));
@@ -561,7 +571,7 @@
         } // end forall windows
 
         if (mWindowDetachedWallpaper != detachedWallpaper) {
-            if (WindowManagerService.DEBUG_WALLPAPER) Slog.v(TAG,
+            if (DEBUG_WALLPAPER) Slog.v(TAG,
                     "Detached wallpaper changed from " + mWindowDetachedWallpaper
                     + " to " + detachedWallpaper);
             mWindowDetachedWallpaper = detachedWallpaper;
@@ -591,7 +601,7 @@
                         if (appAnimator.freezingScreen) {
                             appAnimator.showAllWindowsLocked();
                             mService.unsetAppFreezingScreenLocked(wtoken, false, true);
-                            if (WindowManagerService.DEBUG_ORIENTATION) Slog.i(TAG,
+                            if (DEBUG_ORIENTATION) Slog.i(TAG,
                                     "Setting mOrientationChangeComplete=true because wtoken "
                                     + wtoken + " numInteresting=" + wtoken.numInterestingWindows
                                     + " numDrawn=" + wtoken.numDrawnWindows);
@@ -628,11 +638,11 @@
         boolean wasAnimating = mAnimating;
         mAnimating = false;
         mAppWindowAnimating = false;
-        if (WindowManagerService.DEBUG_WINDOW_TRACE) {
+        if (DEBUG_WINDOW_TRACE) {
             Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
         }
 
-        if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(
+        if (SHOW_TRANSACTIONS) Slog.i(
                 TAG, ">>> OPEN TRANSACTION animateLocked");
         SurfaceControl.openTransaction();
         SurfaceControl.setAnimationTransaction();
@@ -707,7 +717,7 @@
             Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
         } finally {
             SurfaceControl.closeTransaction();
-            if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(
+            if (SHOW_TRANSACTIONS) Slog.i(
                     TAG, "<<< CLOSE TRANSACTION animateLocked");
         }
 
@@ -744,16 +754,40 @@
             }
         }
 
+        if (mRemoveReplacedWindows) {
+            removeReplacedWindowsLocked();
+        }
+
         mService.destroyPreservedSurfaceLocked();
 
-        if (WindowManagerService.DEBUG_WINDOW_TRACE) {
+        if (DEBUG_WINDOW_TRACE) {
             Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating
-                + " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
-                + " mPendingLayoutChanges(DEFAULT_DISPLAY)="
-                + Integer.toHexString(getPendingLayoutChanges(Display.DEFAULT_DISPLAY)));
+                    + " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
+                    + " mPendingLayoutChanges(DEFAULT_DISPLAY)="
+                    + Integer.toHexString(getPendingLayoutChanges(Display.DEFAULT_DISPLAY)));
         }
     }
 
+    private void removeReplacedWindowsLocked() {
+        if (SHOW_TRANSACTIONS) Slog.i(
+                TAG, ">>> OPEN TRANSACTION removeReplacedWindows");
+        SurfaceControl.openTransaction();
+        try {
+            for (int i = mService.mDisplayContents.size() - 1; i >= 0; i--) {
+                DisplayContent display = mService.mDisplayContents.get(i);
+                final WindowList windows = mService.getWindowListLocked(display.getDisplayId());
+                for (int j = windows.size() - 1; j >= 0; j--) {
+                    windows.get(j).maybeRemoveReplacedWindow();
+                }
+            }
+        } finally {
+            SurfaceControl.closeTransaction();
+            if (SHOW_TRANSACTIONS) Slog.i(
+                    TAG, "<<< CLOSE TRANSACTION removeReplacedWindows");
+        }
+        mRemoveReplacedWindows = false;
+    }
+
     private static String bulkUpdateParamsToString(int bulkUpdateParams) {
         StringBuilder builder = new StringBuilder(128);
         if ((bulkUpdateParams & WindowSurfacePlacer.SET_UPDATE_ROTATION) != 0) {
@@ -844,7 +878,7 @@
         for (int i = windows.size() - 1; i >= 0; i--) {
             if (displayId == windows.get(i).getDisplayId()) {
                 setPendingLayoutChanges(displayId, changes);
-                if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
+                if (DEBUG_LAYOUT_REPEATS) {
                     mWindowPlacerLocked.debugLayoutRepeats(reason,
                             getPendingLayoutChanges(displayId));
                 }
@@ -875,6 +909,14 @@
         return getDisplayContentsAnimatorLocked(displayId).mScreenRotationAnimation;
     }
 
+    void requestRemovalOfReplacedWindows(WindowState win) {
+        final AppWindowToken token = win.mAppToken;
+        if (token != null && token.mWillReplaceWindow && token.mReplacingWindow == win) {
+            token.mHasReplacedWindow = true;
+        }
+        mRemoveReplacedWindows = true;
+    }
+
     private class DisplayContentsAnimator {
         ScreenRotationAnimation mScreenRotationAnimation = null;
     }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 5e68aa2..87ad1f88 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -57,7 +57,6 @@
 import android.app.ActivityManagerNative;
 import android.app.AppOpsManager;
 import android.app.IActivityManager;
-import android.app.StatusBarManager;
 import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
@@ -226,6 +225,7 @@
     static final boolean SHOW_SURFACE_ALLOC = false;
     static final boolean SHOW_TRANSACTIONS = false;
     static final boolean SHOW_LIGHT_TRANSACTIONS = false || SHOW_TRANSACTIONS;
+    static final boolean SHOW_VERBOSE_TRANSACTIONS = false && SHOW_TRANSACTIONS;
     static final boolean HIDE_STACK_CRAWLS = true;
     static final int LAYOUT_REPEAT_THRESHOLD = 4;
 
@@ -5627,7 +5627,7 @@
                 }
             }
 
-            if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
+            if (SHOW_VERBOSE_TRANSACTIONS) Slog.i(TAG,
                     ">>> OPEN TRANSACTION showStrictModeViolation");
             SurfaceControl.openTransaction();
             try {
@@ -5639,7 +5639,7 @@
                 mStrictModeFlash.setVisibility(on);
             } finally {
                 SurfaceControl.closeTransaction();
-                if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
+                if (SHOW_VERBOSE_TRANSACTIONS) Slog.i(TAG,
                         "<<< CLOSE TRANSACTION showStrictModeViolation");
             }
         }
@@ -10116,6 +10116,7 @@
             if (DEBUG_ADD_REMOVE) Slog.d(TAG, "Marking app token " + appWindowToken
                     + " as replacing window.");
             appWindowToken.mWillReplaceWindow = true;
+            appWindowToken.mHasReplacedWindow = false;
             appWindowToken.mAnimateReplacingWindow = animate;
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 6d05921..0f1b1f0 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1374,14 +1374,16 @@
 
     void maybeRemoveReplacedWindow() {
         AppWindowToken token = mAppToken;
-        if (token != null && token.mWillReplaceWindow && token.mReplacingWindow == this) {
+        if (token != null && token.mWillReplaceWindow && token.mReplacingWindow == this
+                && token.mHasReplacedWindow) {
             if (DEBUG_ADD_REMOVE) Slog.d(TAG, "Removing replacing window: " + this);
             token.mWillReplaceWindow = false;
             token.mAnimateReplacingWindow = false;
             token.mReplacingRemoveRequested = false;
             token.mReplacingWindow = null;
+            token.mHasReplacedWindow = false;
             for (int i = token.allAppWindows.size() - 1; i >= 0; i--) {
-                WindowState win = token.allAppWindows.get(i);
+                final WindowState win = token.allAppWindows.get(i);
                 if (win.mExiting) {
                     mService.removeWindowInnerLocked(win);
                 }
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 91755ac..81d0b4d 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1287,6 +1287,7 @@
 
             if (prepared && mLastHidden && mDrawState == HAS_DRAWN) {
                 if (showSurfaceRobustlyLocked()) {
+                    mAnimator.requestRemovalOfReplacedWindows(w);
                     mLastHidden = false;
                     if (mIsWallpaper) {
                         mWallpaperControllerLocked.dispatchWallpaperVisibility(w, true);
@@ -1472,12 +1473,8 @@
                 }
                 mWin.mAppToken.updateReportedVisibilityLocked();
             }
-
-            mWin.maybeRemoveReplacedWindow();
-
             return true;
         }
-
         return false;
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 2e2be5c..f8b8d6c 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -23,7 +23,6 @@
 import static com.android.server.wm.WindowManagerService.HIDE_STACK_CRAWLS;
 import static com.android.server.wm.WindowManagerService.DEBUG_VISIBILITY;
 
-import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
@@ -57,7 +56,6 @@
     private float mSurfaceAlpha = 0;
 
     private int mSurfaceLayer = 0;
-    private int mSurfaceFormat;
 
     // Surface flinger doesn't support crop rectangles where width or height is non-positive.
     // However, we need to somehow handle the situation where the cropping would completely hide
@@ -72,7 +70,6 @@
 
         mSurfaceW = w;
         mSurfaceH = h;
-        mSurfaceFormat = format;
 
         title = name;
 
@@ -389,6 +386,11 @@
         pw.print(" x "); pw.println(mSurfaceH);
     }
 
+    @Override
+    public String toString() {
+        return mSurfaceControl.toString();
+    }
+
     static class SurfaceTrace extends SurfaceControl {
         private final static String SURFACE_TAG = "SurfaceTrace";
         private final static boolean LOG_SURFACE_TRACE = DEBUG_SURFACE_TRACE;
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 1f49e97..92ea66b 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -1190,9 +1190,13 @@
                 int layer = -1;
                 for (int j = 0; j < wtoken.windows.size(); j++) {
                     final WindowState win = wtoken.windows.get(j);
-                    // Clearing the mExiting flag before entering animation. It will be set
-                    // to true if app window is removed, or window relayout to invisible.
-                    win.mExiting = false;
+                    // Clearing the mExiting flag before entering animation. It will be set to true
+                    // if app window is removed, or window relayout to invisible. 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.
+                    if (win.mAppToken == null || !win.mAppToken.mWillReplaceWindow) {
+                        win.mExiting = false;
+                    }
                     if (win.mWinAnimator.mAnimLayer > layer) {
                         layer = win.mWinAnimator.mAnimLayer;
                     }
@@ -1232,9 +1236,7 @@
                 true /*updateInputWindows*/);
         mService.mFocusMayChange = false;
         mService.notifyActivityDrawnForKeyguard();
-        return FINISH_LAYOUT_REDO_LAYOUT
-                | FINISH_LAYOUT_REDO_CONFIG;
-
+        return FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_CONFIG;
     }
 
     private boolean transitionGoodToGo(int appsCount) {