Send the bubble removal timestamp to wm shell

The removal and update of bubbles happen in different processes, so
it's possible for race conditions to allow users to remove bubbles
without knowing that an update exists.

Passing the removal timestamp allows wm shell to determine whether
the removal happened before or after the latest update was received
for the bubble. This largely mitigates the race but does not solve
it completely. Will file a separate bug to track that.

Flag: com.android.wm.shell.enable_bubble_bar
Bug: 351026092
Test: Manual
       - Note the issue here is a race condition so requires code
         changes to force repro it
       - Locally I added an artificial delay between the time the
       - bubble is removed and until the removal is sent to wmshell
       - Create bubbles and expand the bubble bar
       - Dismiss bubble by dragging it to dismiss
       - Send an update to the same bubble before removal is processed
         in wmshell
       - Verify bubble is added back
       - Verify bubble is not removed when the removal is processed
         in wmshell
Change-Id: I05ddc692fe30709125e380351b223d20a0778264
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
index a3832cd..7426dc7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
@@ -356,6 +356,13 @@
             }
         }
 
+        // if a bubble was updated upstream, but removed before the update was received, add it back
+        if (update.updatedBubble != null && !mBubbles.containsKey(update.updatedBubble.getKey())) {
+            mBubbles.put(update.updatedBubble.getKey(), update.updatedBubble);
+            mBubbleBarViewController.addBubble(
+                    update.updatedBubble, isExpanding, suppressAnimation);
+        }
+
         if (update.addedBubble != null && isCollapsed) {
             // If we're collapsed, the most recently added bubble will be selected.
             bubbleToSelect = update.addedBubble;
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index ad81509..23c747e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -89,6 +89,8 @@
 
     private BubbleBarViewAnimator mBubbleBarViewAnimator;
 
+    private TimeSource mTimeSource = System::currentTimeMillis;
+
     @Nullable
     private BubbleBarBoundsChangeListener mBoundsChangeListener;
 
@@ -576,7 +578,7 @@
      * @param bubble dismissed bubble item
      */
     public void onDismissBubbleWhileDragging(@NonNull BubbleBarItem bubble) {
-        mSystemUiProxy.dragBubbleToDismiss(bubble.getKey());
+        mSystemUiProxy.dragBubbleToDismiss(bubble.getKey(), mTimeSource.currentTimeMillis());
     }
 
     /**
@@ -601,6 +603,11 @@
         void onBoundsChanged();
     }
 
+    /** Interface for getting the current timestamp. */
+    interface TimeSource {
+        long currentTimeMillis();
+    }
+
     /** Dumps the state of BubbleBarViewController. */
     public void dump(PrintWriter pw) {
         pw.println("Bubble bar view controller state:");
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 433baa9..ec035a9 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -831,12 +831,14 @@
 
     /**
      * Tells SysUI to dismiss the bubble with the provided key.
+     *
      * @param key the key of the bubble to dismiss.
+     * @param timestamp the timestamp when the removal happened.
      */
-    public void dragBubbleToDismiss(String key) {
+    public void dragBubbleToDismiss(String key, long timestamp) {
         if (mBubbles == null) return;
         try {
-            mBubbles.dragBubbleToDismiss(key);
+            mBubbles.dragBubbleToDismiss(key, timestamp);
         } catch (RemoteException e) {
             Log.w(TAG, "Failed call dragBubbleToDismiss");
         }