Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8e4bda9e authored by Chong Zhang's avatar Chong Zhang
Browse files

Fix a flicker when window is removed during entering animation

When animation is started with saved surfaces, app may decide to
remove one of the child windows during early animation and replace
it with a new window. This causes the app below (usually Recents)
to show through for one or more frames.

If we started animation early with a window, delay removal of
that window until the app is allDrawn in the "traditional sense"
(i.e. allDrawn without using any saved surfaces).

bug: 28742353
Change-Id: I4ef663a947b737aae96c3ccfe13a9f4c1d0567f0
parent a63e1325
Loading
Loading
Loading
Loading
+39 −0
Original line number Diff line number Diff line
@@ -88,6 +88,11 @@ class AppWindowToken extends WindowToken {
    // 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 @@ class AppWindowToken extends WindowToken {
        }
    }

    /**
     * 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 @@ class AppWindowToken extends WindowToken {
    void clearAllDrawn() {
        allDrawn = false;
        deferClearAllDrawn = false;
        allDrawnExcludingSaved = false;
    }

    @Override
+1 −0
Original line number Diff line number Diff line
@@ -788,6 +788,7 @@ public class WindowAnimator {
            removeReplacedWindowsLocked();
        }

        mService.stopUsingSavedSurfaceLocked();
        mService.destroyPreservedSurfaceLocked();
        mService.mWindowPlacerLocked.destroyPendingSurfaces();

+33 −0
Original line number Diff line number Diff line
@@ -414,6 +414,13 @@ public class WindowManagerService extends IWindowManager.Stub
     */
    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 @@ public class WindowManagerService extends IWindowManager.Stub
                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();

@@ -8557,6 +8581,15 @@ public class WindowManagerService extends IWindowManager.Stub
        }
        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
    // -------------------------------------------------------------
+35 −1
Original line number Diff line number Diff line
@@ -437,7 +437,9 @@ final class WindowState implements WindowManagerPolicy.WindowState {
    // 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
@@ -1255,6 +1257,32 @@ final class WindowState implements WindowManagerPolicy.WindowState {
        return !mAttachedHidden || mWinAnimator.mAnimation != null;
    }

    /**
     * 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 @@ final class WindowState implements WindowManagerPolicy.WindowState {
        return mAnimatingWithSavedSurface;
    }

    boolean isAnimatingInvisibleWithSavedSurface() {
        return mAnimatingWithSavedSurface
                && (mViewVisibility != View.VISIBLE || mWindowRemovalAllowed);
    }

    public void setVisibleBeforeClientHidden() {
        mWasVisibleBeforeClientHidden |=
                (mViewVisibility == View.VISIBLE || mAnimatingWithSavedSurface);
@@ -2042,6 +2075,7 @@ final class WindowState implements WindowManagerPolicy.WindowState {
            if (mWinAnimator.mSurfaceController != null) {
                mWinAnimator.mSurfaceController.disconnectInTransaction();
            }
            mAnimatingWithSavedSurface = false;
        } else {
            mWinAnimator.destroySurfaceLocked();
        }
+41 −6
Original line number Diff line number Diff line
@@ -792,15 +792,16 @@ class WindowSurfacePlacer {
                            + " 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 @@ class WindowSurfacePlacer {
                            }
                        }
                        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 @@ class WindowSurfacePlacer {
                            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 @@ class WindowSurfacePlacer {
                                    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);
                            }
                        }
                    }
                }
            }
        }