Merge "Clean up surfaces when app is resumed without being stopped." into nyc-mr1-dev
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 9bc0bb4..0601219 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -173,7 +173,8 @@
             in CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
             int icon, int logo, int windowFlags, IBinder transferFrom, boolean createIfNeeded);
     void setAppVisibility(IBinder token, boolean visible);
-    void notifyAppStopped(IBinder token, boolean stopped);
+    void notifyAppResumed(IBinder token, boolean wasStopped);
+    void notifyAppStopped(IBinder token);
     void startAppFreezingScreen(IBinder token, int configChanges);
     void stopAppFreezingScreen(IBinder token, boolean force);
     void removeAppToken(IBinder token);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index eb02dc3..752dbd9 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1249,7 +1249,7 @@
             r.stopped = true;
             r.state = ActivityState.STOPPED;
 
-            mWindowManager.notifyAppStopped(r.appToken, true);
+            mWindowManager.notifyAppStopped(r.appToken);
 
             if (getVisibleBehindActivity() == r) {
                 mStackSupervisor.requestVisibleBehindLocked(r, false);
@@ -2490,7 +2490,7 @@
 
                 // Well the app will no longer be stopped.
                 // Clear app token stopped state in window manager if needed.
-                mWindowManager.notifyAppStopped(next.appToken, false);
+                mWindowManager.notifyAppResumed(next.appToken, next.stopped);
 
                 EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, next.userId,
                         System.identityHashCode(next), next.task.taskId, next.shortComponentName);
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 66e9fd86..eac72b0 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -337,21 +337,41 @@
         }
     }
 
-    // Here we destroy surfaces which have been marked as eligible by the animator, taking care
-    // to ensure the client has finished with them. If the client could still be using them
-    // we will skip destruction and try again when the client has stopped.
     void destroySurfaces() {
+        destroySurfaces(false /*cleanupOnResume*/);
+    }
+
+    /**
+     * Destroy surfaces which have been marked as eligible by the animator, taking care to ensure
+     * the client has finished with them.
+     *
+     * @param cleanupOnResume whether this is done when app is resumed without fully stopped. If
+     * set to true, destroy only surfaces of removed windows, and clear relevant flags of the
+     * others so that they are ready to be reused. If set to false (common case), destroy all
+     * surfaces that's eligible, if the app is already stopped.
+     */
+
+    private void destroySurfaces(boolean cleanupOnResume) {
         final ArrayList<WindowState> allWindows = (ArrayList<WindowState>) allAppWindows.clone();
         final DisplayContentList displayList = new DisplayContentList();
         for (int i = allWindows.size() - 1; i >= 0; i--) {
             final WindowState win = allWindows.get(i);
 
-            if (!(mAppStopped || win.mWindowRemovalAllowed)) {
+            if (!(mAppStopped || win.mWindowRemovalAllowed || cleanupOnResume)) {
                 continue;
             }
 
             win.mWinAnimator.destroyPreservedSurfaceLocked();
 
+            if (cleanupOnResume) {
+                // If the window has an unfinished exit animation, consider that animation
+                // done and mark the window destroying so that it goes through the cleanup.
+                if (win.mAnimatingExit) {
+                    win.mDestroying = true;
+                    win.mAnimatingExit = false;
+                }
+            }
+
             if (!win.mDestroying) {
                 continue;
             }
@@ -361,7 +381,9 @@
                     + " win.mWindowRemovalAllowed=" + win.mWindowRemovalAllowed
                     + " win.mRemoveOnExit=" + win.mRemoveOnExit);
 
-            win.destroyOrSaveSurface();
+            if (!cleanupOnResume || win.mRemoveOnExit) {
+                win.destroyOrSaveSurface();
+            }
             if (win.mRemoveOnExit) {
                 service.removeWindowInnerLocked(win);
             }
@@ -379,21 +401,30 @@
     }
 
     /**
-     * If the application has stopped it is okay to destroy any surfaces which were keeping alive
-     * in case they were still being used.
+     * Notify that the app is now resumed, and it was not stopped before, perform a clean
+     * up of the surfaces
      */
-    void notifyAppStopped(boolean stopped) {
-        if (DEBUG_ADD_REMOVE) Slog.v(TAG, "notifyAppStopped: stopped=" + stopped + " " + this);
-        mAppStopped = stopped;
-
-        if (stopped) {
-            destroySurfaces();
-            // Remove any starting window that was added for this app if they are still around.
-            mTask.mService.scheduleRemoveStartingWindowLocked(this);
+    void notifyAppResumed(boolean wasStopped) {
+        if (DEBUG_ADD_REMOVE) Slog.v(TAG, "notifyAppResumed: wasStopped=" + wasStopped + " " + this);
+        mAppStopped = false;
+        if (!wasStopped) {
+            destroySurfaces(true /*cleanupOnResume*/);
         }
     }
 
     /**
+     * Notify that the app has stopped, and it is okay to destroy any surfaces which were
+     * keeping alive in case they were still being used.
+     */
+    void notifyAppStopped() {
+        if (DEBUG_ADD_REMOVE) Slog.v(TAG, "notifyAppStopped: " + this);
+        mAppStopped = true;
+        destroySurfaces();
+        // Remove any starting window that was added for this app if they are still around.
+        mTask.mService.scheduleRemoveStartingWindowLocked(this);
+    }
+
+    /**
      * Checks whether we should save surfaces for this app.
      *
      * @return true if the surfaces should be saved, false otherwise.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d8a4538..8c5481d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -4476,7 +4476,25 @@
     }
 
     @Override
-    public void notifyAppStopped(IBinder token, boolean stopped) {
+    public void notifyAppResumed(IBinder token, boolean wasStopped) {
+        if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+                "notifyAppResumed()")) {
+            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
+        }
+
+        synchronized(mWindowMap) {
+            final AppWindowToken wtoken;
+            wtoken = findAppWindowToken(token);
+            if (wtoken == null) {
+                Slog.w(TAG_WM, "Attempted to notify resumed of non-existing app token: " + token);
+                return;
+            }
+            wtoken.notifyAppResumed(wasStopped);
+        }
+    }
+
+    @Override
+    public void notifyAppStopped(IBinder token) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "notifyAppStopped()")) {
             throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
@@ -4486,10 +4504,10 @@
             final AppWindowToken wtoken;
             wtoken = findAppWindowToken(token);
             if (wtoken == null) {
-                Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: " + token);
+                Slog.w(TAG_WM, "Attempted to notify stopped of non-existing app token: " + token);
                 return;
             }
-            wtoken.notifyAppStopped(stopped);
+            wtoken.notifyAppStopped();
         }
     }
 
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 49ab9f9..5a9860d 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -351,7 +351,12 @@
     }
 
     @Override
-    public void notifyAppStopped(IBinder token, boolean stopped) throws RemoteException {
+    public void notifyAppResumed(IBinder token, boolean wasStopped) throws RemoteException {
+        // TODO Auto-generated method stub
+    }
+
+    @Override
+    public void notifyAppStopped(IBinder token) throws RemoteException {
         // TODO Auto-generated method stub
     }