From eb22e8ed42bb146060e8ffc94444f70ea47fda04 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Wed, 20 Jan 2016 19:52:22 -0800 Subject: [PATCH] Fixes for broken saved surface - Reset and restore the visibility flags and hasSurface states when surface is saved or restored. When the surface is in saved stated, we have to make the rest of the system believe that the window has no surface. - Set app windows to 'mExiting' when we start a transistion because window manager changes the visibility of the app. We can't rely on receiving a relayoutWindow from the app to invisible. We need to mark it exiting so that when the transition is done, the surfaces get removed (or saved if possible) promptly. - We need to save the surface if the app token is the last one in a task, regardless of whether it's visible, this means the whole task is going into background. But if the app has another visible token on top of it, we don't need to save it. For example one activity launches another activity, in this case we don't want to save the surface of the activity on the bottom. bug: 26573100 Change-Id: Id845f87b30cda1cebcc12ad2ac8dbf19a068a86e --- .../com/android/server/wm/AppWindowToken.java | 14 ++-- .../com/android/server/wm/WindowAnimator.java | 3 +- .../server/wm/WindowManagerService.java | 26 ++------ .../com/android/server/wm/WindowState.java | 64 +++++++++++++------ .../server/wm/WindowStateAnimator.java | 11 +++- .../server/wm/WindowSurfacePlacer.java | 5 +- 6 files changed, 73 insertions(+), 50 deletions(-) diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 785f1660bb067..27328216b45e1 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -302,6 +302,13 @@ class AppWindowToken extends WindowToken { } } + void markSurfacesExiting() { + for (int i = allAppWindows.size() - 1; i >= 0; i--) { + WindowState win = allAppWindows.get(i); + win.mExiting = true; + } + } + /** * Checks whether we should save surfaces for this app. * @@ -329,15 +336,14 @@ class AppWindowToken extends WindowToken { if (!hasSavedSurface()) { return; } - - if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG_WM, - "Restoring saved surfaces: " + this + ", allDrawn=" + allDrawn); - mAnimatingWithSavedSurface = true; for (int i = windows.size() - 1; i >= 0; i--) { WindowState ws = windows.get(i); ws.restoreSavedSurface(); } + // Mark the app allDrawn since it must be allDrawn at the time + // it was first saved. + allDrawn = true; } void destroySavedSurfaces() { diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java index 62d4f3611a74b..722c3b4d35b67 100644 --- a/services/core/java/com/android/server/wm/WindowAnimator.java +++ b/services/core/java/com/android/server/wm/WindowAnimator.java @@ -281,8 +281,7 @@ public class WindowAnimator { final int flags = win.mAttrs.flags; boolean canBeForceHidden = mPolicy.canBeForceHidden(win, win.mAttrs); boolean shouldBeForceHidden = shouldForceHide(win); - if (winAnimator.mSurfaceController != null - && winAnimator.mSurfaceController.hasSurface()) { + if (winAnimator.hasSurface()) { final boolean wasAnimating = winAnimator.mWasAnimating; final boolean nowAnimating = winAnimator.stepAnimationLocked(mCurrentTime); winAnimator.mWasAnimating = nowAnimating; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 4b0015e603751..d2e2639b04cc0 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2679,20 +2679,11 @@ public class WindowManagerService extends IWindowManager.Stub } else { winAnimator.mEnterAnimationPending = false; winAnimator.mEnteringAnimation = false; - if (winAnimator.mSurfaceController != null && - winAnimator.mSurfaceController.hasSurface()) { + if (winAnimator.hasSurface() && !win.mExiting) { if (DEBUG_VISIBILITY) Slog.i(TAG_WM, "Relayout invis " + win + ": mExiting=" + win.mExiting); - // If we are using a saved surface to do enter animation, just let the - // animation run and don't destroy the surface. This could happen when - // the app sets visibility to invisible for the first time after resume, - // or when the user exits immediately after a resume. In both cases, we - // don't want to destroy the saved surface. // If we are not currently running the exit animation, we // need to see about starting one. - final boolean notExitingOrAnimating = - !win.mExiting && !win.isAnimatingWithSavedSurface(); - result |= notExitingOrAnimating ? RELAYOUT_RES_SURFACE_CHANGED : 0; // We don't want to animate visibility of windows which are pending // replacement. In the case of activity relaunch child windows // could request visibility changes as they are detached from the main @@ -2700,11 +2691,11 @@ public class WindowManagerService extends IWindowManager.Stub // these visibility changes though, we would cause a visual glitch // hiding the window before it's replacement was available. // So we just do nothing on our side. - if (notExitingOrAnimating && win.mWillReplaceWindow == false) { - focusMayChange = tryStartingAnimation(win, winAnimator, isDefaultDisplay, - focusMayChange); - + if (!win.mWillReplaceWindow) { + focusMayChange = tryStartExitingAnimation( + win, winAnimator, isDefaultDisplay, focusMayChange); } + result |= RELAYOUT_RES_SURFACE_CHANGED; } outSurface.release(); @@ -2787,7 +2778,7 @@ public class WindowManagerService extends IWindowManager.Stub return result; } - private boolean tryStartingAnimation(WindowState win, WindowStateAnimator winAnimator, + private boolean tryStartExitingAnimation(WindowState win, WindowStateAnimator winAnimator, boolean isDefaultDisplay, boolean focusMayChange) { // Try starting an animation; if there isn't one, we // can destroy the surface right away. @@ -4240,6 +4231,7 @@ public class WindowManagerService extends IWindowManager.Stub } } } else { + wtoken.markSurfacesExiting(); mClosingApps.add(wtoken); wtoken.mEnteringAnimation = false; } @@ -10278,10 +10270,6 @@ public class WindowManagerService extends IWindowManager.Stub return (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, displayMetrics); } - void scheduleSurfaceDestroy(WindowState win) { - mDestroySurface.add(win); - } - @Override public void registerDockedStackListener(IDockedStackListener listener) { if (!checkCallingPermission(android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS, diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index e8a02b0c4e0d9..dca7735f02fc7 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1768,45 +1768,69 @@ final class WindowState implements WindowManagerPolicy.WindowState { return mAppToken != null && mAppToken.mAnimatingWithSavedSurface; } - // Returns true if the surface is saved. - boolean destroyOrSaveSurface() { - Task task = getTask(); + private boolean shouldSaveSurface() { if (ActivityManager.isLowRamDeviceStatic()) { // Don't save surfaces on Svelte devices. - mSurfaceSaved = false; - } else if (task == null || task.inHomeStack() - || task.getTopVisibleAppToken() != mAppToken) { + return false; + } + + if (isChildWindow()) { + return false; + } + + Task task = getTask(); + if (task == null || task.inHomeStack()) { // Don't save surfaces for home stack apps. These usually resume and draw // first frame very fast. Saving surfaces are mostly a waste of memory. - // Don't save if the window is not the topmost window. - mSurfaceSaved = false; - } else if (isChildWindow()) { - mSurfaceSaved = false; - } else { - mSurfaceSaved = mAppToken.shouldSaveSurface(); + return false; } - if (mSurfaceSaved == false) { + + final AppWindowToken taskTop = task.getTopVisibleAppToken(); + if (taskTop != null && taskTop != mAppToken) { + // Don't save if the window is not the topmost window. + return false; + } + + return mAppToken.shouldSaveSurface(); + } + + void destroyOrSaveSurface() { + mSurfaceSaved = shouldSaveSurface(); + if (mSurfaceSaved) { + if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) { + Slog.v(TAG, "Saving surface: " + this); + } + + mWinAnimator.hide("saved surface"); + mWinAnimator.mDrawState = WindowStateAnimator.NO_SURFACE; + setHasSurface(false); + } else { mWinAnimator.destroySurfaceLocked(); } - return mSurfaceSaved; } public void destroySavedSurface() { - if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, "Destroying saved surface: " + this); if (mSurfaceSaved) { + if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) { + Slog.v(TAG, "Destroying saved surface: " + this); + } mWinAnimator.destroySurfaceLocked(); } } + public void restoreSavedSurface() { + mSurfaceSaved = false; + setHasSurface(true); + mWinAnimator.mDrawState = WindowStateAnimator.READY_TO_SHOW; + if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) { + Slog.v(TAG, "Restoring saved surface: " + this); + } + } + public boolean hasSavedSurface() { return mSurfaceSaved; } - public void restoreSavedSurface() { - mSurfaceSaved = false; - mWinAnimator.mDrawState = WindowStateAnimator.READY_TO_SHOW; - } - @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 da4e1913f2628..cffcc5db36ffe 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -468,7 +468,7 @@ class WindowStateAnimator { if (WindowManagerService.localLOGV) Slog.v( TAG, "Exit animation finished in " + this + ": remove=" + mWin.mRemoveOnExit); - if (mSurfaceController != null && mSurfaceController.hasSurface()) { + if (hasSurface()) { mService.mDestroySurface.add(mWin); mWin.mDestroying = true; hide("finishExit"); @@ -734,6 +734,11 @@ class WindowStateAnimator { mTmpSize.bottom += scale * (attrs.surfaceInsets.top + attrs.surfaceInsets.bottom); } + boolean hasSurface() { + return !mWin.mSurfaceSaved + && mSurfaceController != null && mSurfaceController.hasSurface(); + } + void destroySurfaceLocked() { final AppWindowToken wtoken = mWin.mAppToken; if (wtoken != null) { @@ -1229,7 +1234,7 @@ class WindowStateAnimator { void prepareSurfaceLocked(final boolean recoveringMemory) { final WindowState w = mWin; - if (mSurfaceController == null || !mSurfaceController.hasSurface()) { + if (!hasSurface()) { if (w.mOrientationChanging) { if (DEBUG_ORIENTATION) { Slog.v(TAG, "Orientation change skips hidden " + w); @@ -1311,7 +1316,7 @@ class WindowStateAnimator { w.mOrientationChanging = false; } } - if (mSurfaceController != null && mSurfaceController.hasSurface()) { + if (hasSurface()) { w.mToken.hasVisible = true; } } else { diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java index ab6667a2f3839..9012b981cd1a4 100644 --- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java +++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java @@ -1161,6 +1161,9 @@ class WindowSurfacePlacer { appAnimator.animation = null; } wtoken.inPendingTransaction = false; + + wtoken.restoreSavedSurfaces(); + if (!mService.setTokenVisibilityLocked( wtoken, animLp, true, transit, false, voiceInteraction)){ // This token isn't going to be animating. Add it to the list of tokens to @@ -1217,8 +1220,6 @@ class WindowSurfacePlacer { if (mService.mAppTransition.isNextAppTransitionThumbnailUp()) { createThumbnailAppAnimator(transit, wtoken, topOpeningLayer, topClosingLayer); } - - wtoken.restoreSavedSurfaces(); } return topOpeningApp; }