diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java index 49dab0a668150..a37b65ab9b64b 100644 --- a/services/core/java/com/android/server/wm/AppWindowAnimator.java +++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java @@ -185,8 +185,7 @@ public class AppWindowAnimator { clearThumbnail(); setNullAnimation(); if (mAppToken.deferClearAllDrawn) { - mAppToken.allDrawn = false; - mAppToken.deferClearAllDrawn = false; + mAppToken.clearAllDrawn(); } mStackClip = STACK_CLIP_BEFORE_ANIM; } diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index de613aaa94ad2..a23424175300a 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -27,6 +27,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEME 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.WINDOW_REPLACEMENT_TIMEOUT_DURATION; +import static com.android.server.wm.WindowManagerService.H.NOTIFY_ACTIVITY_DRAWN; import com.android.server.input.InputApplicationHandle; import com.android.server.wm.WindowManagerService.H; @@ -65,10 +66,6 @@ class AppWindowToken extends WindowToken { final boolean voiceInteraction; - // Whether we're performing an entering animation with a saved surface. - boolean mAnimatingWithSavedSurface; - - Task mTask; boolean appFullscreen; int requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; @@ -160,6 +157,13 @@ class AppWindowToken extends WindowToken { } } + void setVisibleBeforeClientHidden() { + for (int i = allAppWindows.size() - 1; i >= 0; i--) { + final WindowState w = allAppWindows.get(i); + w.setVisibleBeforeClientHidden(); + } + } + void onFirstWindowDrawn(WindowState win, WindowStateAnimator winAnimator) { firstWindowDrawn = true; @@ -295,7 +299,7 @@ class AppWindowToken extends WindowToken { // 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 || mAnimatingWithSavedSurface + && (win.mViewVisibility == View.VISIBLE || win.isAnimatingWithSavedSurface() || (win.mWinAnimator.isAnimationSet() && !service.mAppTransition.isTransitionSet())) && !win.mDestroying @@ -347,7 +351,6 @@ class AppWindowToken extends WindowToken { win.destroyOrSaveSurface(); if (win.mRemoveOnExit) { - win.mAnimatingExit = false; service.removeWindowInnerLocked(win); } final DisplayContent displayContent = win.getDisplayContent(); @@ -391,43 +394,56 @@ class AppWindowToken extends WindowToken { return allDrawn; } - boolean hasSavedSurface() { + boolean canRestoreSurfaces() { for (int i = allAppWindows.size() -1; i >= 0; i--) { - final WindowState ws = allAppWindows.get(i); - if (ws.hasSavedSurface()) { + final WindowState w = allAppWindows.get(i); + if (w.canRestoreSurface()) { return true; } } return false; } + void clearVisibleBeforeClientHidden() { + for (int i = allAppWindows.size() - 1; i >= 0; i--) { + final WindowState w = allAppWindows.get(i); + w.clearVisibleBeforeClientHidden(); + } + } + void restoreSavedSurfaces() { - if (!hasSavedSurface()) { + if (!canRestoreSurfaces()) { + clearVisibleBeforeClientHidden(); return; } - mAnimatingWithSavedSurface = true; - // Check if we have enough drawn windows to mark allDrawn= true. int numInteresting = 0; int numDrawn = 0; for (int i = allAppWindows.size() - 1; i >= 0; i--) { WindowState w = allAppWindows.get(i); - if (w.hasSavedSurface()) { - w.restoreSavedSurface(); - } - if (w != startingWindow && !w.mAppDied + if (w != startingWindow && !w.mAppDied && w.wasVisibleBeforeClientHidden() && (!mAppAnimator.freezingScreen || !w.mAppFreezing)) { numInteresting++; + if (w.hasSavedSurface()) { + w.restoreSavedSurface(); + } if (w.isDrawnLw()) { numDrawn++; } } } - allDrawn |= (numInteresting > 0) && (numInteresting == numDrawn); + if (!allDrawn) { + allDrawn = (numInteresting > 0) && (numInteresting == numDrawn); + if (allDrawn) { + service.mH.obtainMessage(NOTIFY_ACTIVITY_DRAWN, token).sendToTarget(); + } + } + clearVisibleBeforeClientHidden(); if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.d(TAG, - "restoreSavedSurfaces: " + appWindowToken + " allDrawn=" + allDrawn); + "restoreSavedSurfaces: " + appWindowToken + " allDrawn=" + allDrawn + + " numInteresting=" + numInteresting + " numDrawn=" + numDrawn); } void destroySavedSurfaces() { @@ -435,7 +451,11 @@ class AppWindowToken extends WindowToken { WindowState win = allAppWindows.get(i); win.destroySavedSurface(); } - mAnimatingWithSavedSurface = false; + } + + void clearAllDrawn() { + allDrawn = false; + deferClearAllDrawn = false; } @Override diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 9954f6217d097..a0b8c356cdd87 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2170,7 +2170,7 @@ public class WindowManagerService extends IWindowManager.Stub * Returns true if we're done setting up any transitions. */ private boolean prepareWindowReplacementTransition(AppWindowToken atoken) { - atoken.allDrawn = false; + atoken.clearAllDrawn(); WindowState replacedWindow = null; for (int i = atoken.windows.size() - 1; i >= 0 && replacedWindow == null; i--) { WindowState candidate = atoken.windows.get(i); @@ -2316,7 +2316,7 @@ public class WindowManagerService extends IWindowManager.Stub if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "Preserving " + win + " until the new one is " + "added"); // TODO: We are overloading mAnimatingExit flag to prevent the window state from - // been removed. We probably need another falg to indicate that window removal + // been removed. We probably need another flag to indicate that window removal // should be deffered vs. overloading the flag that says we are playing an exit // animation. win.mAnimatingExit = true; @@ -2464,7 +2464,7 @@ public class WindowManagerService extends IWindowManager.Stub mTokenMap.remove(token.token); } else if (atoken != null) { atoken.firstWindowDrawn = false; - atoken.allDrawn = false; + atoken.clearAllDrawn(); } } @@ -4423,6 +4423,7 @@ public class WindowManagerService extends IWindowManager.Stub // Now that the app is going invisible, we can remove it. It will be restarted // if made visible again. wtoken.removeAllDeadWindows(); + wtoken.setVisibleBeforeClientHidden(); } else if (visible) { if (!mAppTransition.isTransitionSet() && mAppTransition.isReady()) { // Add the app mOpeningApps if transition is unset but ready. This means @@ -4434,8 +4435,7 @@ public class WindowManagerService extends IWindowManager.Stub // If the token is currently hidden (should be the common case), or has been // stopped, then we need to set up to wait for its windows to be ready. if (wtoken.hidden || wtoken.mAppStopped) { - wtoken.allDrawn = false; - wtoken.deferClearAllDrawn = false; + wtoken.clearAllDrawn(); // If the app was already visible, don't reset the waitingToShow state. if (wtoken.hidden) { @@ -9234,8 +9234,7 @@ public class WindowManagerService extends IWindowManager.Stub } winAnimator.mDrawState = DRAW_PENDING; if (w.mAppToken != null) { - w.mAppToken.allDrawn = false; - w.mAppToken.deferClearAllDrawn = false; + w.mAppToken.clearAllDrawn(); } } if (!mResizingWindows.contains(w)) { @@ -9392,7 +9391,7 @@ public class WindowManagerService extends IWindowManager.Stub Slog.w(TAG_WM, "LEAKED SURFACE (app token hidden): " + ws + " surface=" + wsa.mSurfaceController + " token=" + ws.mAppToken - + " saved=" + ws.mAppToken.hasSavedSurface()); + + " saved=" + ws.hasSavedSurface()); if (SHOW_TRANSACTIONS) logSurface(ws, "LEAK DESTROY", false); wsa.destroySurface(); leakedSurface = true; diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index a89b4aadafa61..b66de89f6a2d2 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -435,7 +435,15 @@ final class WindowState implements WindowManagerPolicy.WindowState { // Whether the window has a saved surface from last pause, which can be // used to start an entering animation earlier. - public boolean mSurfaceSaved = false; + private boolean mSurfaceSaved = false; + + // Whether we're performing an entering animation with a saved surface. + private boolean mAnimatingWithSavedSurface; + + // Whether the window was visible when we set the app to invisible last time. WM uses + // this as a hint to restore the surface (if available) for early animation next time + // the app is brought visible. + boolean mWasVisibleBeforeClientHidden; // This window will be replaced due to relaunch. This allows window manager // to differentiate between simple removal of a window and replacement. In the latter case it @@ -1949,7 +1957,20 @@ final class WindowState implements WindowManagerPolicy.WindowState { } boolean isAnimatingWithSavedSurface() { - return mAppToken != null && mAppToken.mAnimatingWithSavedSurface; + return mAnimatingWithSavedSurface; + } + + public void setVisibleBeforeClientHidden() { + mWasVisibleBeforeClientHidden |= + (mViewVisibility == View.VISIBLE || mAnimatingWithSavedSurface); + } + + public void clearVisibleBeforeClientHidden() { + mWasVisibleBeforeClientHidden = false; + } + + public boolean wasVisibleBeforeClientHidden() { + return mWasVisibleBeforeClientHidden; } private boolean shouldSaveSurface() { @@ -1958,6 +1979,10 @@ final class WindowState implements WindowManagerPolicy.WindowState { return false; } + if (!mWasVisibleBeforeClientHidden) { + return false; + } + if ((mAttrs.flags & FLAG_SECURE) != 0) { // We don't save secure surfaces since their content shouldn't be shown while the app // isn't on screen and content might leak through during the transition animation with @@ -2020,18 +2045,22 @@ final class WindowState implements WindowManagerPolicy.WindowState { } else { mWinAnimator.destroySurfaceLocked(); } + // Clear animating flags now, since the surface is now gone. (Note this is true even + // if the surface is saved, to outside world the surface is still NO_SURFACE.) + mAnimatingExit = false; } - public void destroySavedSurface() { + void destroySavedSurface() { if (mSurfaceSaved) { if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) { Slog.v(TAG, "Destroying saved surface: " + this); } mWinAnimator.destroySurfaceLocked(); } + mWasVisibleBeforeClientHidden = false; } - public void restoreSavedSurface() { + void restoreSavedSurface() { if (!mSurfaceSaved) { return; } @@ -2039,6 +2068,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { if (mWinAnimator.mSurfaceController != null) { setHasSurface(true); mWinAnimator.mDrawState = WindowStateAnimator.READY_TO_SHOW; + mAnimatingWithSavedSurface = true; if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) { Slog.v(TAG, "Restoring saved surface: " + this); @@ -2051,10 +2081,30 @@ final class WindowState implements WindowManagerPolicy.WindowState { } } - public boolean hasSavedSurface() { + boolean canRestoreSurface() { + return mWasVisibleBeforeClientHidden && mSurfaceSaved; + } + + boolean hasSavedSurface() { return mSurfaceSaved; } + void clearHasSavedSurface() { + mSurfaceSaved = false; + mAnimatingWithSavedSurface = false; + mWasVisibleBeforeClientHidden = false; + } + + void clearAnimatingWithSavedSurface() { + if (mAnimatingWithSavedSurface) { + // App has drawn something to its windows, we're no longer animating with + // the saved surfaces. + if (DEBUG_ANIM) Slog.d(TAG, + "clearAnimatingWithSavedSurface(): win=" + this); + mAnimatingWithSavedSurface = false; + } + } + @Override public boolean isDefaultDisplay() { final DisplayContent displayContent = getDisplayContent(); diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index f88e30b3c5e57..f0bba4a952d54 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -584,14 +584,8 @@ class WindowStateAnimator { + drawStateToString()); } - if (mWin.mAppToken != null && mWin.mAppToken.mAnimatingWithSavedSurface) { - // App has drawn something to its windows, we're no longer animating with - // the saved surfaces. If the user exits now, we only want to save again - // if allDrawn is true. - if (DEBUG_ANIM) Slog.d(TAG, - "finishDrawingLocked: mAnimatingWithSavedSurface=false " + mWin); - mWin.mAppToken.mAnimatingWithSavedSurface = false; - } + mWin.clearAnimatingWithSavedSurface(); + if (mDrawState == DRAW_PENDING) { if (DEBUG_SURFACE_TRACE || DEBUG_ANIM || SHOW_TRANSACTIONS || DEBUG_ORIENTATION) Slog.v(TAG, "finishDrawingLocked: mDrawState=COMMIT_DRAW_PENDING " + mWin + " in " @@ -687,8 +681,7 @@ class WindowStateAnimator { mDrawState = DRAW_PENDING; if (w.mAppToken != null) { if (w.mAppToken.mAppAnimator.animation == null) { - w.mAppToken.allDrawn = false; - w.mAppToken.deferClearAllDrawn = false; + w.mAppToken.clearAllDrawn(); } else { // Currently animating, persist current state of allDrawn until animation // is complete. @@ -840,20 +833,19 @@ class WindowStateAnimator { } boolean hasSurface() { - return !mWin.mSurfaceSaved + return !mWin.hasSavedSurface() && mSurfaceController != null && mSurfaceController.hasSurface(); } void destroySurfaceLocked() { final AppWindowToken wtoken = mWin.mAppToken; if (wtoken != null) { - wtoken.mAnimatingWithSavedSurface = false; if (mWin == wtoken.startingWindow) { wtoken.startingDisplayed = false; } } - mWin.mSurfaceSaved = false; + mWin.clearHasSavedSurface(); if (mSurfaceController == null) { return;