Several fixes for saved surface

- Do not save if the exiting app is no longer top of task

- Mark saved surfaces as invalid when recoving memory, they will
  be reclaimed if they're hidden.

- Save surface when visibility changed to GONE

- Discard saved surface after rotation

- Misc minor fixes and clean-up

bug: 19940527

Change-Id: I5760c7a7b4bf37ef6bdd39cae793a97cf7579429
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 943e3ea..c5bd3a7 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -17,6 +17,9 @@
 package com.android.server.wm;
 
 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_APP_TRANSITIONS;
+import static com.android.server.wm.WindowManagerService.TAG;
 
 import com.android.server.input.InputApplicationHandle;
 import com.android.server.wm.WindowManagerService.H;
@@ -296,6 +299,52 @@
         }
     }
 
+    /**
+     * Checks whether we should save surfaces for this app.
+     *
+     * @return true if the surfaces should be saved, false otherwise.
+     */
+    boolean shouldSaveSurface() {
+        // We want to save surface if the app's windows are "allDrawn", or if we're
+        // currently animating with save surfaces. (If the app didn't even finish
+        // drawing when the user exits, but we have a saved surface from last time,
+        // we still want to keep that surface.)
+        mHasSavedSurface = allDrawn || mAnimatingWithSavedSurface;
+        if (mHasSavedSurface) {
+            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
+                    "Saving surface: " + this);
+            return true;
+        }
+        return false;
+    }
+
+    void restoreSavedSurfaces() {
+        if (!mHasSavedSurface) {
+            return;
+        }
+
+        if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
+                "Restoring saved surfaces: " + this + ", allDrawn=" + allDrawn);
+
+        mHasSavedSurface = false;
+        mAnimatingWithSavedSurface = true;
+        for (int i = windows.size() - 1; i >= 0; i--) {
+            WindowState ws = windows.get(i);
+            ws.mWinAnimator.mDrawState = WindowStateAnimator.READY_TO_SHOW;
+        }
+    }
+
+    void destroySavedSurfaces() {
+        if (mHasSavedSurface) {
+            if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
+                    "Destroying saved surface: " + this);
+            for (int i = windows.size() - 1; i >= 0; i--) {
+                final WindowState win = windows.get(i);
+                win.mWinAnimator.destroySurfaceLocked();
+            }
+        }
+    }
+
     @Override
     void removeAllWindows() {
         for (int winNdx = allAppWindows.size() - 1; winNdx >= 0;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 4d11452..ad44196 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -342,6 +342,11 @@
         return tokensCount > 0 ? mAppTokens.get(tokensCount - 1).findMainWindow() : null;
     }
 
+    AppWindowToken getTopAppWindowToken() {
+        final int tokensCount = mAppTokens.size();
+        return tokensCount > 0 ? mAppTokens.get(tokensCount - 1) : null;
+    }
+
     @Override
     public boolean isFullscreen() {
         if (useCurrentBounds()) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 47995a7..f31f285 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2637,6 +2637,11 @@
                     }
                 }
                 dragResizing = win.isDragResizing();
+                if (win.isAnimatingWithSavedSurface()) {
+                    // If we're animating with a saved surface now, request client to report draw.
+                    // We still need to know when the real thing is drawn.
+                    toBeDisplayed = true;
+                }
                 try {
                     if (!win.mHasSurface) {
                         surfaceChanged = true;
@@ -2692,11 +2697,9 @@
                     // 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.
-                    final boolean isAnimatingWithSavedSurface =
-                            win.mAppToken != null && win.mAppToken.mAnimatingWithSavedSurface;
                     // If we are not currently running the exit animation, we
                     // need to see about starting one.
-                    if (!win.mExiting && !isAnimatingWithSavedSurface) {
+                    if (!win.mExiting && !win.isAnimatingWithSavedSurface()) {
                         surfaceChanged = true;
                         // Try starting an animation; if there isn't one, we
                         // can destroy the surface right away.
@@ -2722,7 +2725,9 @@
                             if (mInputMethodWindow == win) {
                                 mInputMethodWindow = null;
                             }
-                            winAnimator.destroySurfaceLocked();
+                            if (!win.shouldSaveSurface()) {
+                                winAnimator.destroySurfaceLocked();
+                            }
                         }
                         //TODO (multidisplay): Magnification is supported only for the default
                         if (mAccessibilityController != null
@@ -2855,12 +2860,6 @@
                         getDefaultDisplayContentLocked().pendingLayoutChanges |=
                                 WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
                     }
-                    if (win.mAppToken != null) {
-                        // 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.
-                        win.mAppToken.mAnimatingWithSavedSurface = false;
-                    }
                     win.setDisplayLayoutNeeded();
                     mWindowPlacerLocked.requestTraversal();
                 }
@@ -6112,6 +6111,10 @@
         final WindowList windows = displayContent.getWindowList();
         for (int i = windows.size() - 1; i >= 0; i--) {
             WindowState w = windows.get(i);
+            // Discard surface after orientation change, these can't be reused.
+            if (w.mAppToken != null) {
+                w.mAppToken.destroySavedSurfaces();
+            }
             if (w.mHasSurface) {
                 if (DEBUG_ORIENTATION) Slog.v(TAG, "Set mOrientationChanging of " + w);
                 w.mOrientationChanging = true;
@@ -8767,12 +8770,14 @@
                         } else if (ws.mAppToken != null && ws.mAppToken.clientHidden) {
                             Slog.w(TAG, "LEAKED SURFACE (app token hidden): "
                                     + ws + " surface=" + wsa.mSurfaceControl
-                                    + " token=" + ws.mAppToken);
+                                    + " token=" + ws.mAppToken
+                                    + " saved=" + ws.mAppToken.mHasSavedSurface);
                             if (SHOW_TRANSACTIONS) logSurface(ws, "LEAK DESTROY", null);
                             wsa.mSurfaceControl.destroy();
                             wsa.mSurfaceShown = false;
                             wsa.mSurfaceControl = null;
                             ws.mHasSurface = false;
+                            ws.mAppToken.mHasSavedSurface = false;
                             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 e9ea3a8..1d2cb75 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -29,6 +29,8 @@
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 import static com.android.server.wm.WindowManagerService.DEBUG_ADD_REMOVE;
+import static com.android.server.wm.WindowManagerService.DEBUG_ANIM;
+import static com.android.server.wm.WindowManagerService.DEBUG_APP_TRANSITIONS;
 import static com.android.server.wm.WindowManagerService.DEBUG_CONFIGURATION;
 import static com.android.server.wm.WindowManagerService.DEBUG_LAYOUT;
 import static com.android.server.wm.WindowManagerService.DEBUG_ORIENTATION;
@@ -1505,29 +1507,26 @@
         return mExiting || (mService.mClosingApps.contains(mAppToken));
     }
 
-    boolean saveSurfaceIfNeeded() {
+    boolean isAnimatingWithSavedSurface() {
+        return mAppToken != null && mAppToken.mAnimatingWithSavedSurface;
+    }
+
+    boolean shouldSaveSurface() {
         if (ActivityManager.isLowRamDeviceStatic()) {
             // Don't save surfaces on Svelte devices.
             return false;
         }
 
         Task task = getTask();
-        if (task == null || task.inHomeStack()) {
+        if (task == null || task.inHomeStack()
+                || task.getTopAppWindowToken() != mAppToken) {
             // 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.
             return false;
         }
 
-        // We want to save surface if the app's windows are "allDrawn", or if we're
-        // currently animating with save surfaces. (If the app didn't even finish
-        // drawing when the user exits, but we have a saved surface from last time,
-        // we still want to keep that surface.)
-        if (mAppToken.allDrawn || mAppToken.mAnimatingWithSavedSurface) {
-            mAppToken.mHasSavedSurface = true;
-            return true;
-        }
-
-        return false;
+        return mAppToken.shouldSaveSurface();
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 68c39ba..80f1094 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -525,6 +525,12 @@
             Slog.v(TAG, "Finishing drawing window " + mWin + ": mDrawState="
                     + drawStateToString());
         }
+        if (mWin.mAppToken != null) {
+            // 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.
+            mWin.mAppToken.mAnimatingWithSavedSurface = false;
+        }
         if (mDrawState == DRAW_PENDING) {
             if (DEBUG_SURFACE_TRACE || DEBUG_ANIM || SHOW_TRANSACTIONS || DEBUG_ORIENTATION)
                 Slog.v(TAG, "finishDrawingLocked: mDrawState=COMMIT_DRAW_PENDING " + this + " in "
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index b267860..aca0f5b 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -363,8 +363,8 @@
                 continue;
             }
             // Discard the saved surface if window size is changed, it can't be reused.
-            if (win.mAppToken != null && win.mAppToken.mHasSavedSurface) {
-                win.mWinAnimator.destroySurfaceLocked();
+            if (win.mAppToken != null) {
+                win.mAppToken.destroySavedSurfaces();
             }
             win.reportResized();
             mService.mResizingWindows.remove(i);
@@ -397,7 +397,7 @@
                 if (mWallpaperControllerLocked.isWallpaperTarget(win)) {
                     wallpaperDestroyed = true;
                 }
-                if (!win.saveSurfaceIfNeeded()) {
+                if (!win.shouldSaveSurface()) {
                     win.mWinAnimator.destroySurfaceLocked();
                 }
             } while (i > 0);
@@ -1191,7 +1191,10 @@
             if (animLp != null) {
                 int layer = -1;
                 for (int j = 0; j < wtoken.windows.size(); j++) {
-                    WindowState win = wtoken.windows.get(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;
                     if (win.mWinAnimator.mAnimLayer > layer) {
                         layer = win.mWinAnimator.mAnimLayer;
                     }
@@ -1203,17 +1206,7 @@
             }
             createThumbnailAppAnimator(transit, wtoken, topOpeningLayer, topClosingLayer);
 
-            if (wtoken.mHasSavedSurface) {
-                if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
-                        "Early start of animation: " + wtoken + ", allDrawn=" + wtoken.allDrawn);
-
-                for (int j = 0; j < wtoken.windows.size(); j++) {
-                    WindowState ws = wtoken.windows.get(j);
-                    ws.mWinAnimator.mDrawState = WindowStateAnimator.READY_TO_SHOW;
-                }
-                wtoken.mHasSavedSurface = false;
-                wtoken.mAnimatingWithSavedSurface = true;
-            }
+            wtoken.restoreSavedSurfaces();
         }
 
         AppWindowAnimator openingAppAnimator = (topOpeningApp == null) ?  null :