Merge "Replace SurfaceViews across resize trigerred relaunches."
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index a7d3669..93122dd 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4325,6 +4325,21 @@
 
         r.activity.mChangingConfigurations = true;
 
+        // If we are preserving the main window across relaunches we would also like to preserve
+        // the children. However the client side view system does not support preserving
+        // the child views so we notify the window manager to expect these windows to
+        // be replaced and defer requests to destroy or hide them. This way we can achieve
+        // visual continuity. It's important that we do this here prior to pause and destroy
+        // as that is when we may hide or remove the child views.
+        try {
+            if (r.mPreserveWindow) {
+                WindowManagerGlobal.getWindowSession().prepareToReplaceChildren(r.token);
+            }
+        } catch (RemoteException e) {
+            // If the system process has died, it's game over for everyone.
+        }
+
+
         // Need to ensure state is saved.
         if (!r.paused) {
             performPauseActivity(r.token, false, r.isPreHoneycomb());
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index bea36c0..1a5de7e 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -123,6 +123,14 @@
     void repositionChild(IWindow childWindow, int left, int top, int right, int bottom,
             long deferTransactionUntilFrame, out Rect outFrame);
 
+    /*
+     * Notify the window manager that an application is relaunching and
+     * child windows should be prepared for replacement.
+     *
+     * @param appToken The application
+     */
+    void prepareToReplaceChildren(IBinder appToken);
+
     /**
      * If a call to relayout() asked to have the surface destroy deferred,
      * it must call this once it is okay to destroy that surface.
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 46240783..785f166 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -405,6 +405,17 @@
         }
     }
 
+    void setReplacingChildren() {
+        if (DEBUG_ADD_REMOVE) Slog.d(TAG_WM, "Marking app token " + appWindowToken
+                + " with replacing child windows.");
+        for (int i = allAppWindows.size() - 1; i >= 0; i--) {
+            final WindowState w = allAppWindows.get(i);
+            if (w.isChildWindow()) {
+                w.setReplacing(false /* animate */);
+            }
+        }
+    }
+
     void resetReplacingWindows() {
         if (DEBUG_ADD_REMOVE) Slog.d(TAG_WM, "Resetting app token " + appWindowToken
                 + " of replacing window marks.");
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index ac38424..8aaf430 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -200,6 +200,11 @@
                 deferTransactionUntilFrame, outFrame);
     }
 
+    @Override
+    public void prepareToReplaceChildren(IBinder appToken) {
+        mService.setReplacingChildren(appToken);
+    }
+
     public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
             int requestedWidth, int requestedHeight, int viewFlags,
             int flags, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d05a61a..4b0015e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2693,7 +2693,14 @@
                     final boolean notExitingOrAnimating =
                             !win.mExiting && !win.isAnimatingWithSavedSurface();
                     result |= notExitingOrAnimating ? RELAYOUT_RES_SURFACE_CHANGED : 0;
-                    if (notExitingOrAnimating) {
+                    // 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
+                    // application window during the tear down process. If we satisfied
+                    // 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);
 
@@ -2893,9 +2900,10 @@
         try {
             synchronized (mWindowMap) {
                 WindowState win = windowForClientLocked(session, client, false);
-                if (win == null) {
+                if (win == null || win.mWillReplaceWindow) {
                     return;
                 }
+
                 win.mWinAnimator.destroyDeferredSurfaceLocked();
             }
         } finally {
@@ -10184,6 +10192,27 @@
     }
 
     /**
+     * Hint to a token that its children will be replaced across activity relaunch.
+     * The children would otherwise be removed  shortly following this as the
+     * activity is torn down.
+     * @param token Application token for which the activity will be relaunched.
+     */
+    public void setReplacingChildren(IBinder token) {
+        AppWindowToken appWindowToken = null;
+        synchronized (mWindowMap) {
+            appWindowToken = findAppWindowToken(token);
+            if (appWindowToken == null || !appWindowToken.isVisible()) {
+                Slog.w(TAG_WM, "Attempted to set replacing window on non-existing app token "
+                        + token);
+                return;
+            }
+
+            appWindowToken.setReplacingChildren();
+            scheduleClearReplacingWindowIfNeeded(token, true /* replacing */);
+        }
+    }
+
+    /**
      * If we're replacing the window, schedule a timer to clear the replaced window
      * after a timeout, in case the replacing window is not coming.
      *
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
index aae5ac4..769285f 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
@@ -231,4 +231,9 @@
     public void pokeDrawLock(IBinder window) {
         // pass for now.
     }
+
+    @Override
+    public void prepareToReplaceChildren(IBinder appToken) {
+        // pass for now.
+    }
 }