Move surface save state tracking to WindowState.

In the current set up, surface saving is managed by the app window
token. So when destroySavedSurfaces is called, all saved surfaces
assosciated with a given app will be destroyed. This causes pretty weird
behavior where hiding child windows can destroy the parent window. We
move this tracking to WindowState and allow child windows to exempt themselves.

Bug: 25780116
Change-Id: I3ab92221d83297092dfd98a71e6a5fe96381de8b
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 3d00e02..23ad1a81b 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -54,10 +54,6 @@
 
     final boolean voiceInteraction;
 
-    // Whether the window has a saved surface from last pause, which can be
-    // used to start an entering animation earlier.
-    boolean mHasSavedSurface;
-
     // Whether we're performing an entering animation with a saved surface.
     boolean mAnimatingWithSavedSurface;
 
@@ -316,39 +312,38 @@
         // 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 allDrawn || mAnimatingWithSavedSurface;
+    }
+
+    boolean hasSavedSurface() {
+        for (int i = windows.size() -1; i >= 0; i--) {
+            final WindowState ws = windows.get(i);
+            if (ws.hasSavedSurface()) {
+                return true;
+            }
         }
         return false;
     }
 
     void restoreSavedSurfaces() {
-        if (!mHasSavedSurface) {
+        if (!hasSavedSurface()) {
             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;
+            ws.restoreSavedSurface();
         }
     }
 
     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();
-            }
+        for (int i = windows.size() - 1; i >= 0; i--) {
+            WindowState win = windows.get(i);
+            win.destroySavedSurface();
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index de1f4d1..4fa844f 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2814,9 +2814,7 @@
             if (mInputMethodWindow == win) {
                 mInputMethodWindow = null;
             }
-            if (!win.shouldSaveSurface()) {
-                winAnimator.destroySurfaceLocked();
-            }
+            win.destroyOrSaveSurface();
         }
         //TODO (multidisplay): Magnification is supported only for the default
         if (mAccessibilityController != null
@@ -8966,11 +8964,10 @@
                             Slog.w(TAG, "LEAKED SURFACE (app token hidden): "
                                     + ws + " surface=" + wsa.mSurfaceController
                                     + " token=" + ws.mAppToken
-                                    + " saved=" + ws.mAppToken.mHasSavedSurface);
+                                    + " saved=" + ws.mAppToken.hasSavedSurface());
                             if (SHOW_TRANSACTIONS) logSurface(ws, "LEAK DESTROY", null);
                             wsa.destroySurface();
                             ws.setHasSurface(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 5e38492..c9ded3a 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -84,6 +84,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_FOCUS_LIGHT;
 import static com.android.server.wm.WindowManagerService.DEBUG_LAYOUT;
@@ -402,6 +404,10 @@
     /** When true this window can be displayed on screens owther than mOwnerUid's */
     private boolean mShowToOwnerOnly;
 
+    // Whether the window has a saved surface from last pause, which can be
+    // used to start an entering animation earlier.
+    public boolean mSurfaceSaved = false;
+
     /**
      * Wake lock for drawing.
      * Even though it's slightly more expensive to do so, we will use a separate wake lock
@@ -1670,22 +1676,45 @@
         return mAppToken != null && mAppToken.mAnimatingWithSavedSurface;
     }
 
-    boolean shouldSaveSurface() {
+    // Returns true if the surface is saved.
+    boolean destroyOrSaveSurface() {
+        Task task = getTask();
         if (ActivityManager.isLowRamDeviceStatic()) {
             // Don't save surfaces on Svelte devices.
-            return false;
-        }
-
-        Task task = getTask();
-        if (task == null || task.inHomeStack()
+            mSurfaceSaved = false;
+        } else if (task == null || task.inHomeStack()
                 || task.getTopVisibleAppToken() != 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;
+            mSurfaceSaved = false;
+        } else if (mAttachedWindow != null) {
+            mSurfaceSaved = false;
+        } else {
+            mSurfaceSaved = mAppToken.shouldSaveSurface();
         }
+        if (mSurfaceSaved == false) {
+            mWinAnimator.destroySurfaceLocked();
+        }
+        return mSurfaceSaved;
+    }
 
-        return mAppToken.shouldSaveSurface();
+    public void destroySavedSurface() {
+        if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
+                "Destroying saved surface: " + this);
+
+        if (mSurfaceSaved) {
+            mWinAnimator.destroySurfaceLocked();
+        }
+    }
+
+    public boolean hasSavedSurface() {
+        return mSurfaceSaved;
+    }
+
+    public void restoreSavedSurface() {
+        mSurfaceSaved = false;
+        mWinAnimator.mDrawState = WindowStateAnimator.READY_TO_SHOW;
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 132b1b6..1790fb3 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -726,13 +726,14 @@
     void destroySurfaceLocked() {
         final AppWindowToken wtoken = mWin.mAppToken;
         if (wtoken != null) {
-            wtoken.mHasSavedSurface = false;
             wtoken.mAnimatingWithSavedSurface = false;
             if (mWin == wtoken.startingWindow) {
                 wtoken.startingDisplayed = false;
             }
         }
 
+        mWin.mSurfaceSaved = false;
+
         if (mSurfaceController != null) {
             int i = mWin.mChildWindows.size();
             // When destroying a surface we want to make sure child windows
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 2149019..3ae3be5 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -394,9 +394,7 @@
                 if (mWallpaperControllerLocked.isWallpaperTarget(win)) {
                     wallpaperDestroyed = true;
                 }
-                if (!win.shouldSaveSurface()) {
-                    win.mWinAnimator.destroySurfaceLocked();
-                }
+                win.destroyOrSaveSurface();
             } while (i > 0);
             mService.mDestroySurface.clear();
         }
@@ -1252,7 +1250,7 @@
                         + wtoken.startingDisplayed + " startingMoved="
                         + wtoken.startingMoved);
 
-                if (wtoken.mHasSavedSurface || wtoken.mAnimatingWithSavedSurface) {
+                if (wtoken.hasSavedSurface() || wtoken.mAnimatingWithSavedSurface) {
                     continue;
                 }
                 if (!wtoken.allDrawn && !wtoken.startingDisplayed && !wtoken.startingMoved) {