Some fixes for docking from navigation bar use case

- Do not set replace window if we are keeping the window.

- After we received the app's addWindow request, do a reset of the
timeout timer to give the app more time to finish drawing. This
reduces the chance of the flash due to the old window being removed
before new window is drawn.

- If we really hit timeout limit, make sure the replaced window
is removed by using removeWindowInnerLocked(), not removeWindowLocked().
The latter does not actually remove the window if it's exiting,
and this leaves the window in the stack and marked not for
for replacing, then it can never be removed.

bug: 26324082

Change-Id: I59594798079481cba9490c4754de1b16533e72fe
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 573aaec..751f871 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -25,6 +25,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
 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 com.android.server.input.InputApplicationHandle;
 import com.android.server.wm.WindowManagerService.H;
@@ -403,12 +404,28 @@
         }
     }
 
+    void resetReplacingWindows() {
+        if (DEBUG_ADD_REMOVE) Slog.d(TAG_WM, "Resetting app token " + appWindowToken
+                + " of replacing window marks.");
+
+        for (int i = allAppWindows.size() - 1; i >= 0; i--) {
+            final WindowState w = allAppWindows.get(i);
+            w.resetReplacing();
+        }
+    }
+
     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;
+
+                // if we got a replacement window, reset the timeout to give drawing more time
+                service.mH.removeMessages(H.WINDOW_REPLACEMENT_TIMEOUT);
+                service.mH.sendMessageDelayed(
+                        service.mH.obtainMessage(H.WINDOW_REPLACEMENT_TIMEOUT, this),
+                            WINDOW_REPLACEMENT_TIMEOUT_DURATION);
             }
         }
         allAppWindows.add(w);
@@ -424,7 +441,7 @@
         return false;
     }
 
-    void clearTimedoutReplaceesLocked() {
+    void clearTimedoutReplacesLocked() {
         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
@@ -437,7 +454,11 @@
                 continue;
             }
             candidate.mWillReplaceWindow = false;
-            service.removeWindowLocked(candidate);
+            // Since the window already timed out, remove it immediately now.
+            // Use removeWindowInnerLocked() instead of removeWindowLocked(), as the latter
+            // delays removal on certain conditions, which will leave the stale window in the
+            // stack and marked mWillReplaceWindow=false, so the window will never be removed.
+            service.removeWindowInnerLocked(candidate);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d722ec7..213f14b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8106,7 +8106,7 @@
                 case WINDOW_REPLACEMENT_TIMEOUT: {
                     final AppWindowToken token = (AppWindowToken) msg.obj;
                     synchronized (mWindowMap) {
-                        token.clearTimedoutReplaceesLocked();
+                        token.clearTimedoutReplacesLocked();
                     }
                 }
                 break;
@@ -10192,11 +10192,34 @@
             }
             appWindowToken.setReplacingWindows(animate);
         }
+    }
 
-        if (appWindowToken != null) {
-            mH.removeMessages(H.WINDOW_REPLACEMENT_TIMEOUT);
-            mH.sendMessageDelayed(mH.obtainMessage(H.WINDOW_REPLACEMENT_TIMEOUT, appWindowToken),
-                    WINDOW_REPLACEMENT_TIMEOUT_DURATION);
+    /**
+     * 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.
+     *
+     * If we're not replacing the window, clear the replace window settings of the app.
+     *
+     * @param token Application token for the activity whose window might be replaced.
+     * @param replacing Whether the window is being replaced or not.
+     */
+    public void scheduleClearReplacingWindowIfNeeded(IBinder token, boolean replacing) {
+        AppWindowToken appWindowToken = null;
+        synchronized (mWindowMap) {
+            appWindowToken = findAppWindowToken(token);
+            if (appWindowToken == null) {
+                Slog.w(TAG_WM, "Attempted to reset replacing window on non-existing app token "
+                        + token);
+                return;
+            }
+            if (replacing) {
+                mH.removeMessages(H.WINDOW_REPLACEMENT_TIMEOUT);
+                mH.sendMessageDelayed(
+                        mH.obtainMessage(H.WINDOW_REPLACEMENT_TIMEOUT, appWindowToken),
+                        WINDOW_REPLACEMENT_TIMEOUT_DURATION);
+            } else {
+                appWindowToken.resetReplacingWindows();
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 6e4e01f..b7fd60f 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2363,4 +2363,10 @@
         mReplacingWindow = null;
         mAnimateReplacingWindow = animate;
     }
+
+    void resetReplacing() {
+        mWillReplaceWindow = false;
+        mReplacingWindow = null;
+        mAnimateReplacingWindow = false;
+    }
 }