[Media TTT] Handle multiple devices

Handles multiple devices so the view of the most active recent device is
displayed. Adding a queue to store the active device states. Whenever
there is a new device is trying to transfer media, it has a certain
state in the queue and stayed in the queue until it gets removed when
remove view is called.

Bug: 213576772
Test: atest ChipbarCoordinatorTest
Test: atest TemporaryViewLoggerTest
Test: atest TemporaryViewDisplayControllerTest
Test: atest MediaTttChipControllerReceiverTest
Change-Id: I3b2502f045281d01b51215d58bbb7d3759d04de2
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
index a4a96806..647beb9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
@@ -61,7 +61,7 @@
             @SuppressLint("WrongConstant") // sysui allowed to call STATUS_BAR_SERVICE
             val statusBarManager = context.getSystemService(Context.STATUS_BAR_SERVICE)
                     as StatusBarManager
-            val routeInfo = MediaRoute2Info.Builder("id", args[0])
+            val routeInfo = MediaRoute2Info.Builder(if (args.size >= 4) args[3] else "id", args[0])
                     .addFeature("feature")
             val useAppIcon = !(args.size >= 3 && args[2] == "useAppIcon=false")
             if (useAppIcon) {
@@ -107,7 +107,7 @@
 
         override fun help(pw: PrintWriter) {
             pw.println("Usage: adb shell cmd statusbar $SENDER_COMMAND " +
-                    "<deviceName> <chipState> useAppIcon=[true|false]")
+                    "<deviceName> <chipState> useAppIcon=[true|false] <id>")
         }
     }
 
@@ -127,8 +127,10 @@
             @SuppressLint("WrongConstant") // sysui is allowed to call STATUS_BAR_SERVICE
             val statusBarManager = context.getSystemService(Context.STATUS_BAR_SERVICE)
                     as StatusBarManager
-            val routeInfo = MediaRoute2Info.Builder("id", "Test Name")
-                .addFeature("feature")
+            val routeInfo = MediaRoute2Info.Builder(
+                if (args.size >= 3) args[2] else "id",
+                "Test Name"
+            ).addFeature("feature")
             val useAppIcon = !(args.size >= 2 && args[1] == "useAppIcon=false")
             if (useAppIcon) {
                 routeInfo.setClientPackageName(TEST_PACKAGE_NAME)
@@ -144,7 +146,7 @@
 
         override fun help(pw: PrintWriter) {
             pw.println("Usage: adb shell cmd statusbar $RECEIVER_COMMAND " +
-                    "<chipState> useAppIcon=[true|false]")
+                    "<chipState> useAppIcon=[true|false] <id>")
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 8bddffc..691953a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -121,18 +121,32 @@
         uiEventLogger.logReceiverStateChange(chipState)
 
         if (chipState == ChipStateReceiver.FAR_FROM_SENDER) {
-            removeView(removalReason = ChipStateReceiver.FAR_FROM_SENDER.name)
+            removeView(routeInfo.id, removalReason = ChipStateReceiver.FAR_FROM_SENDER.name)
             return
         }
         if (appIcon == null) {
-            displayView(ChipReceiverInfo(routeInfo, appIconDrawableOverride = null, appName))
+            displayView(
+                ChipReceiverInfo(
+                    routeInfo,
+                    appIconDrawableOverride = null,
+                    appName,
+                    id = routeInfo.id,
+                )
+            )
             return
         }
 
         appIcon.loadDrawableAsync(
                 context,
                 Icon.OnDrawableLoadedListener { drawable ->
-                    displayView(ChipReceiverInfo(routeInfo, drawable, appName))
+                    displayView(
+                        ChipReceiverInfo(
+                            routeInfo,
+                            drawable,
+                            appName,
+                            id = routeInfo.id,
+                        )
+                    )
                 },
                 // Notify the listener on the main handler since the listener will update
                 // the UI.
@@ -234,4 +248,5 @@
     val appNameOverride: CharSequence?,
     override val windowTitle: String = MediaTttUtils.WINDOW_TITLE_RECEIVER,
     override val wakeReason: String = MediaTttUtils.WAKE_REASON_RECEIVER,
+    override val id: String,
 ) : TemporaryViewInfo()
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
index d1ea2d0..bb7bc6f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinator.kt
@@ -108,7 +108,7 @@
             }
 
             displayedState = null
-            chipbarCoordinator.removeView(removalReason)
+            chipbarCoordinator.removeView(routeInfo.id, removalReason)
         } else {
             displayedState = chipState
             chipbarCoordinator.displayView(
@@ -162,6 +162,7 @@
             windowTitle = MediaTttUtils.WINDOW_TITLE_SENDER,
             wakeReason = MediaTttUtils.WAKE_REASON_SENDER,
             timeoutMs = chipStateSender.timeout,
+            id = routeInfo.id,
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
index 8270336..a9d05d1 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
@@ -94,6 +94,13 @@
     private var wakeReasonAcquired: String? = null
 
     /**
+     * A stack of pairs of device id and temporary view info. This is used when there may be
+     * multiple devices in range, and we want to always display the chip for the most recently
+     * active device.
+     */
+    internal val activeViews: ArrayDeque<Pair<String, T>> = ArrayDeque()
+
+    /**
      * Displays the view with the provided [newInfo].
      *
      * This method handles inflating and attaching the view, then delegates to [updateView] to
@@ -102,6 +109,12 @@
     fun displayView(newInfo: T) {
         val currentDisplayInfo = displayInfo
 
+        // Update our list of active devices by removing it if necessary, then adding back at the
+        // front of the list
+        val id = newInfo.id
+        val position = findAndRemoveFromActiveViewsList(id)
+        activeViews.addFirst(Pair(id, newInfo))
+
         if (currentDisplayInfo != null &&
             currentDisplayInfo.info.windowTitle == newInfo.windowTitle) {
             // We're already displaying information in the correctly-titled window, so we just need
@@ -113,7 +126,10 @@
                 // We're already displaying information but that information is under a different
                 // window title. So, we need to remove the old window with the old title and add a
                 // new window with the new title.
-                removeView(removalReason = "New info has new window title: ${newInfo.windowTitle}")
+                removeView(
+                    id,
+                    removalReason = "New info has new window title: ${newInfo.windowTitle}"
+                )
             }
 
             // At this point, we're guaranteed to no longer be displaying a view.
@@ -140,7 +156,7 @@
             }
             wakeLock?.acquire(newInfo.wakeReason)
             wakeReasonAcquired = newInfo.wakeReason
-            logger.logViewAddition(newInfo.windowTitle)
+            logger.logViewAddition(id, newInfo.windowTitle)
             inflateAndUpdateView(newInfo)
         }
 
@@ -151,9 +167,13 @@
             // include it just to be safe.
             FLAG_CONTENT_ICONS or FLAG_CONTENT_TEXT or FLAG_CONTENT_CONTROLS
        )
-        cancelViewTimeout?.run()
+
+        // Only cancel timeout of the most recent view displayed, as it will be reset.
+        if (position == 0) {
+            cancelViewTimeout?.run()
+        }
         cancelViewTimeout = mainExecutor.executeDelayed(
-            { removeView(REMOVAL_REASON_TIMEOUT) },
+            { removeView(id, REMOVAL_REASON_TIMEOUT) },
             timeout.toLong()
         )
     }
@@ -196,28 +216,67 @@
     }
 
     /**
-     * Hides the view.
+     * Hides the view given its [id].
      *
+     * @param id the id of the device responsible of displaying the temp view.
      * @param removalReason a short string describing why the view was removed (timeout, state
      *     change, etc.)
      */
-    fun removeView(removalReason: String) {
+    fun removeView(id: String, removalReason: String) {
         val currentDisplayInfo = displayInfo ?: return
 
+        val removalPosition = findAndRemoveFromActiveViewsList(id)
+        if (removalPosition == null) {
+            logger.logViewRemovalIgnored(id, "view not found in the list")
+            return
+        }
+        if (removalPosition != 0) {
+            logger.logViewRemovalIgnored(id, "most recent view is being displayed.")
+            return
+        }
+        logger.logViewRemoval(id, removalReason)
+
+        val newViewToDisplay = if (activeViews.isEmpty()) {
+            null
+        } else {
+            activeViews[0].second
+        }
+
         val currentView = currentDisplayInfo.view
         animateViewOut(currentView) {
             windowManager.removeView(currentView)
             wakeLock?.release(wakeReasonAcquired)
         }
 
-        logger.logViewRemoval(removalReason)
         configurationController.removeCallback(displayScaleListener)
         // Re-set to null immediately (instead as part of the animation end runnable) so
-        // that if a new view event comes in while this view is animating out, we still display the
-        // new view appropriately.
+        // that if a new view event comes in while this view is animating out, we still display
+        // the new view appropriately.
         displayInfo = null
         // No need to time the view out since it's already gone
         cancelViewTimeout?.run()
+
+        if (newViewToDisplay != null) {
+            mainExecutor.executeDelayed({ displayView(newViewToDisplay)}, DISPLAY_VIEW_DELAY)
+        }
+    }
+
+    /**
+     * Finds and removes the active view with the given [id] from the stack, or null if there is no
+     * active view with that ID
+     *
+     * @param id that temporary view belonged to.
+     *
+     * @return index of the view in the stack , otherwise null.
+     */
+    private fun findAndRemoveFromActiveViewsList(id: String): Int? {
+        for (i in 0 until activeViews.size) {
+            if (activeViews[i].first == id) {
+                activeViews.removeAt(i)
+                return i
+            }
+        }
+        return null
     }
 
     /**
@@ -258,6 +317,7 @@
 }
 
 private const val REMOVAL_REASON_TIMEOUT = "TIMEOUT"
+const val DISPLAY_VIEW_DELAY = 50L
 
 private data class IconInfo(
     val iconName: String,
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewInfo.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewInfo.kt
index cbb5002..df83960 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewInfo.kt
@@ -37,6 +37,11 @@
      * disappears.
      */
     open val timeoutMs: Int = DEFAULT_TIMEOUT_MILLIS
+
+    /**
+     * The id of the temporary view.
+     */
+    abstract val id: String
 }
 
 const val DEFAULT_TIMEOUT_MILLIS = 10000
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewLogger.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewLogger.kt
index 428a104..133a384 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewLogger.kt
@@ -24,13 +24,42 @@
     internal val buffer: LogBuffer,
     internal val tag: String,
 ) {
-    /** Logs that we added the view in a window titled [windowTitle]. */
-    fun logViewAddition(windowTitle: String) {
-        buffer.log(tag, LogLevel.DEBUG, { str1 = windowTitle }, { "View added. window=$str1" })
+    /** Logs that we added the view with the given [id] in a window titled [windowTitle]. */
+    fun logViewAddition(id: String, windowTitle: String) {
+        buffer.log(
+            tag,
+            LogLevel.DEBUG,
+            {
+                str1 = windowTitle
+                str2 = id
+            },
+            { "View added. window=$str1 id=$str2" }
+        )
     }
 
-    /** Logs that we removed the chip for the given [reason]. */
-    fun logViewRemoval(reason: String) {
-        buffer.log(tag, LogLevel.DEBUG, { str1 = reason }, { "View removed due to: $str1" })
+    /** Logs that we removed the view with the given [id] for the given [reason]. */
+    fun logViewRemoval(id: String, reason: String) {
+        buffer.log(
+            tag,
+            LogLevel.DEBUG,
+            {
+                str1 = reason
+                str2 = id
+            },
+            { "View with id=$str2 is removed due to: $str1" }
+        )
+    }
+
+    /** Logs that we ignored removal of the view with the given [id]. */
+    fun logViewRemovalIgnored(id: String, reason: String) {
+        buffer.log(
+            tag,
+            LogLevel.DEBUG,
+            {
+                str1 = reason
+                str2 = id
+            },
+            { "Removal of view with id=$str2 is ignored because $str1" }
+        )
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt
index 6237365..b92e0ec 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt
@@ -40,6 +40,7 @@
     override val windowTitle: String,
     override val wakeReason: String,
     override val timeoutMs: Int,
+    override val id: String,
 ) : TemporaryViewInfo()
 
 /** The possible items to display at the end of the chipbar. */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index 68a5f47..885cc54 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -261,7 +261,12 @@
     @Test
     fun updateView_noOverrides_usesInfoFromAppIcon() {
         controllerReceiver.displayView(
-            ChipReceiverInfo(routeInfo, appIconDrawableOverride = null, appNameOverride = null)
+            ChipReceiverInfo(
+                routeInfo,
+                appIconDrawableOverride = null,
+                appNameOverride = null,
+                id = "id",
+            )
         )
 
         val view = getChipView()
@@ -274,7 +279,12 @@
         val drawableOverride = context.getDrawable(R.drawable.ic_celebration)!!
 
         controllerReceiver.displayView(
-            ChipReceiverInfo(routeInfo, drawableOverride, appNameOverride = null)
+            ChipReceiverInfo(
+                routeInfo,
+                drawableOverride,
+                appNameOverride = null,
+                id = "id",
+            )
         )
 
         val view = getChipView()
@@ -286,7 +296,12 @@
         val appNameOverride = "Sweet New App"
 
         controllerReceiver.displayView(
-            ChipReceiverInfo(routeInfo, appIconDrawableOverride = null, appNameOverride)
+            ChipReceiverInfo(
+                routeInfo,
+                appIconDrawableOverride = null,
+                appNameOverride,
+                id = "id",
+            )
         )
 
         val view = getChipView()
@@ -340,7 +355,7 @@
             .addFeature("feature")
             .setClientPackageName(packageName)
             .build()
-        return ChipReceiverInfo(routeInfo, null, null)
+        return ChipReceiverInfo(routeInfo, null, null, id = "id")
     }
 
     private fun ViewGroup.getAppIconView() = this.requireViewById<ImageView>(R.id.app_icon)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
index 8572478..09f0d4a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
@@ -119,7 +119,7 @@
             )
         )
 
-        verify(logger).logViewAddition("Fake Window Title")
+        verify(logger).logViewAddition("id", "Fake Window Title")
     }
 
     @Test
@@ -153,7 +153,7 @@
         underTest.displayView(getState())
         assertThat(fakeWakeLock.isHeld).isTrue()
 
-        underTest.removeView("test reason")
+        underTest.removeView("id", "test reason")
 
         assertThat(fakeWakeLock.isHeld).isFalse()
     }
@@ -263,21 +263,143 @@
     }
 
     @Test
+    fun multipleViewsWithDifferentIds_recentActiveViewIsDisplayed() {
+        underTest.displayView(ViewInfo("First name", id = "id1"))
+
+        verify(windowManager).addView(any(), any())
+
+        reset(windowManager)
+        underTest.displayView(ViewInfo("Second name", id = "id2"))
+        underTest.removeView("id2", "test reason")
+
+        verify(windowManager).removeView(any())
+
+        fakeClock.advanceTime(DISPLAY_VIEW_DELAY + 1)
+
+        assertThat(underTest.mostRecentViewInfo?.id).isEqualTo("id1")
+        assertThat(underTest.mostRecentViewInfo?.name).isEqualTo("First name")
+
+        reset(windowManager)
+        fakeClock.advanceTime(TIMEOUT_MS + 1)
+
+        verify(windowManager).removeView(any())
+        assertThat(underTest.activeViews.size).isEqualTo(0)
+    }
+
+    @Test
+    fun multipleViewsWithDifferentIds_oldViewRemoved_recentViewIsDisplayed() {
+        underTest.displayView(ViewInfo("First name", id = "id1"))
+
+        verify(windowManager).addView(any(), any())
+
+        reset(windowManager)
+        underTest.displayView(ViewInfo("Second name", id = "id2"))
+        underTest.removeView("id1", "test reason")
+
+        verify(windowManager, never()).removeView(any())
+        assertThat(underTest.mostRecentViewInfo?.id).isEqualTo("id2")
+        assertThat(underTest.mostRecentViewInfo?.name).isEqualTo("Second name")
+
+        fakeClock.advanceTime(TIMEOUT_MS + 1)
+
+        verify(windowManager).removeView(any())
+        assertThat(underTest.activeViews.size).isEqualTo(0)
+    }
+
+    @Test
+    fun multipleViewsWithDifferentIds_threeDifferentViews_recentActiveViewIsDisplayed() {
+        underTest.displayView(ViewInfo("First name", id = "id1"))
+        underTest.displayView(ViewInfo("Second name", id = "id2"))
+        underTest.displayView(ViewInfo("Third name", id = "id3"))
+
+        verify(windowManager).addView(any(), any())
+
+        reset(windowManager)
+        underTest.removeView("id3", "test reason")
+
+        verify(windowManager).removeView(any())
+
+        fakeClock.advanceTime(DISPLAY_VIEW_DELAY + 1)
+
+        assertThat(underTest.mostRecentViewInfo?.id).isEqualTo("id2")
+        assertThat(underTest.mostRecentViewInfo?.name).isEqualTo("Second name")
+
+        reset(windowManager)
+        underTest.removeView("id2", "test reason")
+
+        verify(windowManager).removeView(any())
+
+        fakeClock.advanceTime(DISPLAY_VIEW_DELAY + 1)
+
+        assertThat(underTest.mostRecentViewInfo?.id).isEqualTo("id1")
+        assertThat(underTest.mostRecentViewInfo?.name).isEqualTo("First name")
+
+        reset(windowManager)
+        fakeClock.advanceTime(TIMEOUT_MS + 1)
+
+        verify(windowManager).removeView(any())
+        assertThat(underTest.activeViews.size).isEqualTo(0)
+    }
+
+    @Test
+    fun multipleViewsWithDifferentIds_oneViewStateChanged_stackHasRecentState() {
+        underTest.displayView(ViewInfo("First name", id = "id1"))
+        underTest.displayView(ViewInfo("New name", id = "id1"))
+
+        verify(windowManager).addView(any(), any())
+
+        reset(windowManager)
+        underTest.displayView(ViewInfo("Second name", id = "id2"))
+        underTest.removeView("id2", "test reason")
+
+        verify(windowManager).removeView(any())
+
+        fakeClock.advanceTime(DISPLAY_VIEW_DELAY + 1)
+
+        assertThat(underTest.mostRecentViewInfo?.id).isEqualTo("id1")
+        assertThat(underTest.mostRecentViewInfo?.name).isEqualTo("New name")
+        assertThat(underTest.activeViews[0].second.name).isEqualTo("New name")
+
+        reset(windowManager)
+        fakeClock.advanceTime(TIMEOUT_MS + 1)
+
+        verify(windowManager).removeView(any())
+        assertThat(underTest.activeViews.size).isEqualTo(0)
+    }
+
+    @Test
+    fun multipleViewsWithDifferentIds_viewsTimeouts_noViewLeftToDisplay() {
+        underTest.displayView(ViewInfo("First name", id = "id1"))
+        fakeClock.advanceTime(TIMEOUT_MS / 3)
+        underTest.displayView(ViewInfo("Second name", id = "id2"))
+        fakeClock.advanceTime(TIMEOUT_MS / 3)
+        underTest.displayView(ViewInfo("Third name", id = "id3"))
+
+        reset(windowManager)
+        fakeClock.advanceTime(TIMEOUT_MS + 1)
+
+        verify(windowManager).removeView(any())
+        verify(windowManager, never()).addView(any(), any())
+        assertThat(underTest.activeViews.size).isEqualTo(0)
+    }
+
+    @Test
     fun removeView_viewRemovedAndRemovalLogged() {
         // First, add the view
         underTest.displayView(getState())
 
         // Then, remove it
         val reason = "test reason"
-        underTest.removeView(reason)
+        val deviceId = "id"
+        underTest.removeView(deviceId, reason)
 
         verify(windowManager).removeView(any())
-        verify(logger).logViewRemoval(reason)
+        verify(logger).logViewRemoval(deviceId, reason)
     }
 
     @Test
     fun removeView_noAdd_viewNotRemoved() {
-        underTest.removeView("reason")
+        underTest.removeView("id", "reason")
 
         verify(windowManager, never()).removeView(any())
     }
@@ -329,7 +451,8 @@
         val name: String,
         override val windowTitle: String = "Window Title",
         override val wakeReason: String = "WAKE_REASON",
-        override val timeoutMs: Int = 1
+        override val timeoutMs: Int = 1,
+        override val id: String = "id",
     ) : TemporaryViewInfo()
 }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewLoggerTest.kt
index d155050..116b8fe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewLoggerTest.kt
@@ -44,7 +44,7 @@
 
     @Test
     fun logViewAddition_bufferHasLog() {
-        logger.logViewAddition("Test Window Title")
+        logger.logViewAddition("test id", "Test Window Title")
 
         val stringWriter = StringWriter()
         buffer.dump(PrintWriter(stringWriter), tailLength = 0)
@@ -57,7 +57,8 @@
     @Test
     fun logViewRemoval_bufferHasTagAndReason() {
         val reason = "test reason"
-        logger.logViewRemoval(reason)
+        val deviceId = "test id"
+        logger.logViewRemoval(deviceId, reason)
 
         val stringWriter = StringWriter()
         buffer.dump(PrintWriter(stringWriter), tailLength = 0)
@@ -65,6 +66,7 @@
 
         assertThat(actualString).contains(TAG)
         assertThat(actualString).contains(reason)
+        assertThat(actualString).contains(deviceId)
     }
 }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
index 8e37aa2..47c84ab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
@@ -377,6 +377,7 @@
             windowTitle = WINDOW_TITLE,
             wakeReason = WAKE_REASON,
             timeoutMs = TIMEOUT,
+            id = DEVICE_ID,
         )
     }
 
@@ -401,3 +402,4 @@
 private const val TIMEOUT = 10000
 private const val WINDOW_TITLE = "Test Chipbar Window Title"
 private const val WAKE_REASON = "TEST_CHIPBAR_WAKE_REASON"
+private const val DEVICE_ID = "id"