Fix replacing window timeouts

Do not post app token with the timeout message, instead put the token
in a list and post empty message only.

bug: 28744782
Change-Id: If809d8ee16bcc31067f25ae5696b62d09ea4b864
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index a234241..e490a40 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -582,10 +582,7 @@
                 w.mSkipEnterAnimationForSeamlessReplacement = !candidate.mAnimateReplacingWindow;
 
                 // 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);
+                service.scheduleReplacingWindowTimeouts(this);
             }
         }
         allAppWindows.add(w);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 196b38a..8706682 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -415,6 +415,12 @@
     final ArrayList<AppWindowToken> mFinishedStarting = new ArrayList<>();
 
     /**
+     * List of app window tokens that are waiting for replacing windows. If the
+     * replacement doesn't come in time the stale windows needs to be disposed of.
+     */
+    final ArrayList<AppWindowToken> mReplacingWindowTimeouts = new ArrayList<>();
+
+    /**
      * The input consumer added to the window manager which consumes input events to windows below
      * it.
      */
@@ -8497,9 +8503,12 @@
                 }
                 break;
                 case WINDOW_REPLACEMENT_TIMEOUT: {
-                    final AppWindowToken token = (AppWindowToken) msg.obj;
                     synchronized (mWindowMap) {
-                        token.clearTimedoutReplacesLocked();
+                        for (int i = mReplacingWindowTimeouts.size() - 1; i >= 0; i--) {
+                            final AppWindowToken token = mReplacingWindowTimeouts.get(i);
+                            token.clearTimedoutReplacesLocked();
+                        }
+                        mReplacingWindowTimeouts.clear();
                     }
                 }
                 case NOTIFY_APP_TRANSITION_STARTING: {
@@ -10757,16 +10766,22 @@
                 return;
             }
             if (replacing) {
-                mH.removeMessages(H.WINDOW_REPLACEMENT_TIMEOUT);
-                mH.sendMessageDelayed(
-                        mH.obtainMessage(H.WINDOW_REPLACEMENT_TIMEOUT, appWindowToken),
-                        WINDOW_REPLACEMENT_TIMEOUT_DURATION);
+                scheduleReplacingWindowTimeouts(appWindowToken);
             } else {
                 appWindowToken.resetReplacingWindows();
             }
         }
     }
 
+    void scheduleReplacingWindowTimeouts(AppWindowToken appWindowToken) {
+        if (!mReplacingWindowTimeouts.contains(appWindowToken)) {
+            mReplacingWindowTimeouts.add(appWindowToken);
+        }
+        mH.removeMessages(H.WINDOW_REPLACEMENT_TIMEOUT);
+        mH.sendEmptyMessageDelayed(
+                H.WINDOW_REPLACEMENT_TIMEOUT, WINDOW_REPLACEMENT_TIMEOUT_DURATION);
+    }
+
     @Override
     public int getDockedStackSide() {
         synchronized (mWindowMap) {