Fixes for restoring more than one child surfaces

App may have more than one windows and subwindows. Remember which ones
are visible at the last setAppVisibility time, and restore only those
that was visible then. If the app itself requested to hide a window
before that, we don't want to use the window for early animation.

Also move mAnimatingWithSavedSurface into WindowState as it needs to
be tracked per window.

bug: 27455025
bug: 28296945

Change-Id: I3ed1879634fa7709de699d4518d8fcfc91a85554
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index 49dab0a..a37b65a 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 @@
         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 de613aa..a234241 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.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 @@
 
     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 @@
         }
     }
 
+    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 @@
             // 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 @@
 
             win.destroyOrSaveSurface();
             if (win.mRemoveOnExit) {
-                win.mAnimatingExit = false;
                 service.removeWindowInnerLocked(win);
             }
             final DisplayContent displayContent = win.getDisplayContent();
@@ -391,43 +394,56 @@
         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 @@
             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 be888fe..2fe7b84 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 @@
      * 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 @@
                 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 @@
                 mTokenMap.remove(token.token);
             } else if (atoken != null) {
                 atoken.firstWindowDrawn = false;
-                atoken.allDrawn = false;
+                atoken.clearAllDrawn();
             }
         }
 
@@ -4423,6 +4423,7 @@
                 // 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 @@
                 // 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) {
@@ -9233,8 +9233,7 @@
                     }
                     winAnimator.mDrawState = DRAW_PENDING;
                     if (w.mAppToken != null) {
-                        w.mAppToken.allDrawn = false;
-                        w.mAppToken.deferClearAllDrawn = false;
+                        w.mAppToken.clearAllDrawn();
                     }
                 }
                 if (!mResizingWindows.contains(w)) {
@@ -9391,7 +9390,7 @@
                         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 a89b4aa..b66de89 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 @@
 
     // 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 @@
     }
 
     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 @@
             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 @@
         } 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 @@
         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 @@
         }
     }
 
-    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 f88e30b..f0bba4a 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 @@
                     + 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 @@
         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 @@
     }
 
     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;