Move window replacement tracking to window state.

In preparation for supporting replacement of child windows
we make replacement per window rather than per app.

Bug: 26070641

Change-Id: Ifa332086599c125611e430219c9497bae7e2ce31
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index fd5c704..b49641f 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -19,6 +19,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -122,21 +123,6 @@
     // True if the windows associated with this token should be cropped to their stack bounds.
     boolean mCropWindowsToStack;
 
-    // This application will have its window replaced due to relaunch. This allows window manager
-    // to differentiate between simple removal of a window and replacement. In the latter case it
-    // will preserve the old window until the new one is drawn.
-    boolean mWillReplaceWindow;
-    // If true, the replaced window was already requested to be removed.
-    boolean mReplacingRemoveRequested;
-    // Whether the replacement of the window should trigger app transition animation.
-    boolean mAnimateReplacingWindow;
-    // If not null, the window that will be used to replace the old one. This is being set when
-    // the window is added and unset when this window reports its first draw.
-    WindowState mReplacingWindow;
-    // Whether the new window has replaced the old one, so the old one can be removed without
-    // blinking.
-    boolean mHasReplacedWindow;
-
     AppWindowToken(WindowManagerService _service, IApplicationToken _token,
             boolean _voiceInteraction) {
         super(_service, _token.asBinder(),
@@ -392,6 +378,62 @@
         }
     }
 
+    void setReplacingWindows(boolean animate) {
+        if (DEBUG_ADD_REMOVE) Slog.d(TAG_WM, "Marking app token " + appWindowToken
+                + " with replacing windows.");
+
+        for (int i = allAppWindows.size() - 1; i >= 0; i--) {
+            final WindowState w = allAppWindows.get(i);
+            w.setReplacing(animate);
+        }
+        if (animate) {
+            // Set-up dummy animation so we can start treating windows associated with this
+            // token like they are in transition before the new app window is ready for us to
+            // run the real transition animation.
+            if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM,
+                    "setReplacingWindow() Setting dummy animation on: " + this);
+            mAppAnimator.setDummyAnimation();
+        }
+    }
+
+    void addWindow(WindowState w) {
+        for (int i = allAppWindows.size() - 1; i >= 0; i--) {
+            WindowState candidate = allAppWindows.get(i);
+            if (candidate.mWillReplaceWindow && candidate.mReplacingWindow == null &&
+                    candidate.getWindowTag().equals(w.getWindowTag().toString())) {
+                candidate.mReplacingWindow = w;
+            }
+        }
+        allAppWindows.add(w);
+    }
+
+    boolean waitingForReplacement() {
+        for (int i = allAppWindows.size() -1; i >= 0; i--) {
+            WindowState candidate = allAppWindows.get(i);
+            if (candidate.mWillReplaceWindow) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    void clearTimedoutReplaceesLocked() {
+        for (int i = allAppWindows.size() - 1; i >= 0;
+             // removeWindowLocked at bottom of loop may remove multiple entries from
+             // allAppWindows if the window to be removed has child windows. It also may
+             // not remove any windows from allAppWindows at all if win is exiting and
+             // currently animating away. This ensures that winNdx is monotonically decreasing
+             // and never beyond allAppWindows bounds.
+             i = Math.min(i - 1, allAppWindows.size() - 1)) {
+            WindowState candidate = allAppWindows.get(i);
+            if (candidate.mWillReplaceWindow == false) {
+                continue;
+            }
+            candidate.mWillReplaceWindow = false;
+            service.removeWindowLocked(candidate);
+        }
+    }
+
     @Override
     void dump(PrintWriter pw, String prefix) {
         super.dump(pw, prefix);