Merge "Update ScreenRecorder to handle StopReason" into main
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 02c7901..049189f 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -28277,7 +28277,7 @@
         mPrivateFlags |= PFLAG_FORCE_LAYOUT;
         mPrivateFlags |= PFLAG_INVALIDATED;
 
-        if (mParent != null) {
+        if (mParent != null && !mParent.isLayoutRequested()) {
             mParent.requestLayout();
         }
         if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index 81734a3..d0d4af6 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -116,3 +116,14 @@
     description: "Relax the assumption of non-match parent activity"
     bug: "356277166"
 }
+
+flag {
+    namespace: "windowing_sdk"
+    name: "allow_multiple_adjacent_task_fragments"
+    description: "Refactor to allow more than 2 adjacent TaskFragments"
+    bug: "373709676"
+    is_fixed_read_only: true
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/core/tests/coretests/src/android/view/ViewGroupTest.java b/core/tests/coretests/src/android/view/ViewGroupTest.java
index 43c404e..ae3ad36 100644
--- a/core/tests/coretests/src/android/view/ViewGroupTest.java
+++ b/core/tests/coretests/src/android/view/ViewGroupTest.java
@@ -213,35 +213,6 @@
         assertTrue(autofillableViews.containsAll(Arrays.asList(viewA, viewC)));
     }
 
-    @Test
-    public void testMeasureCache() {
-        final int spec1 = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.AT_MOST);
-        final int spec2 = View.MeasureSpec.makeMeasureSpec(50, View.MeasureSpec.AT_MOST);
-        final Context context = getInstrumentation().getContext();
-        final View child = new View(context);
-        final TestView parent = new TestView(context, 0);
-        parent.addView(child);
-
-        child.setPadding(1, 2, 3, 4);
-        parent.measure(spec1, spec1);
-        assertEquals(4, parent.getMeasuredWidth());
-        assertEquals(6, parent.getMeasuredHeight());
-
-        child.setPadding(5, 6, 7, 8);
-        parent.measure(spec2, spec2);
-        assertEquals(12, parent.getMeasuredWidth());
-        assertEquals(14, parent.getMeasuredHeight());
-
-        // This ends the state of forceLayout.
-        parent.layout(0, 0, 50, 50);
-
-        // The cached values should be cleared after the new setPadding is called. And the measured
-        // width and height should be up-to-date.
-        parent.measure(spec1, spec1);
-        assertEquals(12, parent.getMeasuredWidth());
-        assertEquals(14, parent.getMeasuredHeight());
-    }
-
     private static void getUnobscuredTouchableRegion(Region outRegion, View view) {
         outRegion.set(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
         final ViewParent parent = view.getParent();
@@ -269,19 +240,6 @@
         protected void onLayout(boolean changed, int l, int t, int r, int b) {
             // We don't layout this view.
         }
-
-        @Override
-        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-            int measuredWidth = 0;
-            int measuredHeight = 0;
-            final int count = getChildCount();
-            for (int i = 0; i < count; i++) {
-                final View child = getChildAt(i);
-                measuredWidth += child.getPaddingLeft() + child.getPaddingRight();
-                measuredHeight += child.getPaddingTop() + child.getPaddingBottom();
-            }
-            setMeasuredDimension(measuredWidth, measuredHeight);
-        }
     }
 
     public static class AutofillableTestView extends TestView {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplOldTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplOldTest.kt
new file mode 100644
index 0000000..44a8518
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplOldTest.kt
@@ -0,0 +1,684 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.notification.headsup
+
+import android.app.Notification
+import android.app.PendingIntent
+import android.app.Person
+import android.os.Handler
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.FlagsParameterization
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.kosmos.KosmosJavaAdapter
+import com.android.systemui.log.logcatLogBuffer
+import com.android.systemui.res.R
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManagerImpl
+import com.android.systemui.statusbar.notification.headsup.HeadsUpManagerImpl.HeadsUpEntry
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.kotlin.JavaAdapter
+import com.android.systemui.util.settings.FakeGlobalSettings
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.MutableStateFlow
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.invocation.InvocationOnMock
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.eq
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+
+@SmallTest
+@RunWithLooper
+@RunWith(ParameterizedAndroidJunit4::class)
+// TODO(b/378142453): Merge this with HeadsUpManagerImplTest.
+open class HeadsUpManagerImplOldTest(flags: FlagsParameterization?) : SysuiTestCase() {
+    protected var mKosmos: KosmosJavaAdapter = KosmosJavaAdapter(this)
+
+    @JvmField @Rule var rule: MockitoRule = MockitoJUnit.rule()
+
+    private val mUiEventLoggerFake = UiEventLoggerFake()
+
+    private val mLogger: HeadsUpManagerLogger = Mockito.spy(HeadsUpManagerLogger(logcatLogBuffer()))
+
+    @Mock private val mBgHandler: Handler? = null
+
+    @Mock private val dumpManager: DumpManager? = null
+
+    @Mock private val mShadeInteractor: ShadeInteractor? = null
+    private var mAvalancheController: AvalancheController? = null
+
+    @Mock private val mAccessibilityMgr: AccessibilityManagerWrapper? = null
+
+    protected val mGlobalSettings: FakeGlobalSettings = FakeGlobalSettings()
+    protected val mSystemClock: FakeSystemClock = FakeSystemClock()
+    protected val mExecutor: FakeExecutor = FakeExecutor(mSystemClock)
+
+    @Mock protected var mRow: ExpandableNotificationRow? = null
+
+    private fun createHeadsUpManager(): HeadsUpManagerImpl {
+        return TestableHeadsUpManager(
+            mContext,
+            mLogger,
+            mKosmos.statusBarStateController,
+            mKosmos.keyguardBypassController,
+            GroupMembershipManagerImpl(),
+            mKosmos.visualStabilityProvider,
+            mKosmos.configurationController,
+            mExecutor,
+            mGlobalSettings,
+            mSystemClock,
+            mAccessibilityMgr,
+            mUiEventLoggerFake,
+            JavaAdapter(mKosmos.testScope),
+            mShadeInteractor,
+            mAvalancheController,
+        )
+    }
+
+    private fun createStickyEntry(id: Int): NotificationEntry {
+        val notif =
+            Notification.Builder(mContext, "")
+                .setSmallIcon(R.drawable.ic_person)
+                .setFullScreenIntent(
+                    Mockito.mock(PendingIntent::class.java), /* highPriority */
+                    true,
+                )
+                .build()
+        return HeadsUpManagerTestUtil.createEntry(id, notif)
+    }
+
+    private fun createStickyForSomeTimeEntry(id: Int): NotificationEntry {
+        val notif =
+            Notification.Builder(mContext, "")
+                .setSmallIcon(R.drawable.ic_person)
+                .setFlag(Notification.FLAG_FSI_REQUESTED_BUT_DENIED, true)
+                .build()
+        return HeadsUpManagerTestUtil.createEntry(id, notif)
+    }
+
+    private fun useAccessibilityTimeout(use: Boolean) {
+        if (use) {
+            Mockito.doReturn(TEST_A11Y_AUTO_DISMISS_TIME)
+                .`when`(mAccessibilityMgr!!)
+                .getRecommendedTimeoutMillis(ArgumentMatchers.anyInt(), ArgumentMatchers.anyInt())
+        } else {
+            Mockito.`when`(
+                    mAccessibilityMgr!!.getRecommendedTimeoutMillis(
+                        ArgumentMatchers.anyInt(),
+                        ArgumentMatchers.anyInt(),
+                    )
+                )
+                .then { i: InvocationOnMock -> i.getArgument(0) }
+        }
+    }
+
+    init {
+        mSetFlagsRule.setFlagsParameterization(flags!!)
+    }
+
+    @Throws(Exception::class)
+    override fun SysuiSetup() {
+        super.SysuiSetup()
+        mAvalancheController =
+            AvalancheController(dumpManager!!, mUiEventLoggerFake, mLogger, mBgHandler!!)
+        Mockito.`when`(mShadeInteractor!!.isAnyExpanded).thenReturn(MutableStateFlow(true))
+        Mockito.`when`(mKosmos.keyguardBypassController.bypassEnabled).thenReturn(false)
+    }
+
+    @Test
+    fun testHasNotifications_headsUpManagerMapNotEmpty_true() {
+        val bhum = createHeadsUpManager()
+        val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+        bhum.showNotification(entry)
+
+        Truth.assertThat(bhum.mHeadsUpEntryMap).isNotEmpty()
+        Truth.assertThat(bhum.hasNotifications()).isTrue()
+    }
+
+    @Test
+    @EnableFlags(NotificationThrottleHun.FLAG_NAME)
+    fun testHasNotifications_avalancheMapNotEmpty_true() {
+        val bhum = createHeadsUpManager()
+        val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+        val headsUpEntry = bhum.createHeadsUpEntry(notifEntry)
+        mAvalancheController!!.addToNext(headsUpEntry) {}
+
+        Truth.assertThat(mAvalancheController!!.getWaitingEntryList()).isNotEmpty()
+        Truth.assertThat(bhum.hasNotifications()).isTrue()
+    }
+
+    @Test
+    @EnableFlags(NotificationThrottleHun.FLAG_NAME)
+    fun testHasNotifications_false() {
+        val bhum = createHeadsUpManager()
+        Truth.assertThat(bhum.mHeadsUpEntryMap).isEmpty()
+        Truth.assertThat(mAvalancheController!!.getWaitingEntryList()).isEmpty()
+        Truth.assertThat(bhum.hasNotifications()).isFalse()
+    }
+
+    @Test
+    @EnableFlags(NotificationThrottleHun.FLAG_NAME)
+    fun testGetHeadsUpEntryList_includesAvalancheEntryList() {
+        val bhum = createHeadsUpManager()
+        val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+        val headsUpEntry = bhum.createHeadsUpEntry(notifEntry)
+        mAvalancheController!!.addToNext(headsUpEntry) {}
+
+        Truth.assertThat(bhum.headsUpEntryList).contains(headsUpEntry)
+    }
+
+    @Test
+    @EnableFlags(NotificationThrottleHun.FLAG_NAME)
+    fun testGetHeadsUpEntry_returnsAvalancheEntry() {
+        val bhum = createHeadsUpManager()
+        val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+        val headsUpEntry = bhum.createHeadsUpEntry(notifEntry)
+        mAvalancheController!!.addToNext(headsUpEntry) {}
+
+        Truth.assertThat(bhum.getHeadsUpEntry(notifEntry.key)).isEqualTo(headsUpEntry)
+    }
+
+    @Test
+    fun testShowNotification_addsEntry() {
+        val alm = createHeadsUpManager()
+        val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+        alm.showNotification(entry)
+
+        assertThat(alm.isHeadsUpEntry(entry.key)).isTrue()
+        assertThat(alm.hasNotifications()).isTrue()
+        assertThat(alm.getEntry(entry.key)).isEqualTo(entry)
+    }
+
+    @Test
+    fun testShowNotification_autoDismisses() {
+        val alm = createHeadsUpManager()
+        val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+        alm.showNotification(entry)
+        mSystemClock.advanceTime((TEST_AUTO_DISMISS_TIME * 3 / 2).toLong())
+
+        assertThat(alm.isHeadsUpEntry(entry.key)).isFalse()
+    }
+
+    @Test
+    fun testRemoveNotification_removeDeferred() {
+        val alm = createHeadsUpManager()
+        val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+        alm.showNotification(entry)
+
+        val removedImmediately =
+            alm.removeNotification(entry.key, /* releaseImmediately= */ false, "removeDeferred")
+        assertThat(removedImmediately).isFalse()
+        assertThat(alm.isHeadsUpEntry(entry.key)).isTrue()
+    }
+
+    @Test
+    fun testRemoveNotification_forceRemove() {
+        val alm = createHeadsUpManager()
+        val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+        alm.showNotification(entry)
+
+        val removedImmediately =
+            alm.removeNotification(entry.key, /* releaseImmediately= */ true, "forceRemove")
+        assertThat(removedImmediately).isTrue()
+        assertThat(alm.isHeadsUpEntry(entry.key)).isFalse()
+    }
+
+    @Test
+    fun testReleaseAllImmediately() {
+        val alm = createHeadsUpManager()
+        for (i in 0 until TEST_NUM_NOTIFICATIONS) {
+            val entry = HeadsUpManagerTestUtil.createEntry(i, mContext)
+            entry.row = mRow
+            alm.showNotification(entry)
+        }
+
+        alm.releaseAllImmediately()
+
+        assertThat(alm.allEntries.count()).isEqualTo(0)
+    }
+
+    @Test
+    fun testCanRemoveImmediately_notShownLongEnough() {
+        val alm = createHeadsUpManager()
+        val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+        alm.showNotification(entry)
+
+        // The entry has just been added so we should not remove immediately.
+        assertThat(alm.canRemoveImmediately(entry.key)).isFalse()
+    }
+
+    @Test
+    fun testHunRemovedLogging() {
+        val hum = createHeadsUpManager()
+        val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+        val headsUpEntry = Mockito.mock(HeadsUpEntry::class.java)
+        Mockito.`when`(headsUpEntry.pinnedStatus)
+            .thenReturn(MutableStateFlow(PinnedStatus.NotPinned))
+        headsUpEntry.mEntry = notifEntry
+
+        hum.onEntryRemoved(headsUpEntry, "test")
+
+        Mockito.verify(mLogger, Mockito.times(1)).logNotificationActuallyRemoved(eq(notifEntry))
+    }
+
+    @Test
+    fun testShowNotification_autoDismissesIncludingTouchAcceptanceDelay() {
+        val hum = createHeadsUpManager()
+        val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+        useAccessibilityTimeout(false)
+
+        hum.showNotification(entry)
+        mSystemClock.advanceTime((TEST_TOUCH_ACCEPTANCE_TIME / 2 + TEST_AUTO_DISMISS_TIME).toLong())
+
+        assertThat(hum.isHeadsUpEntry(entry.key)).isTrue()
+    }
+
+    @Test
+    fun testShowNotification_autoDismissesWithDefaultTimeout() {
+        val hum = createHeadsUpManager()
+        val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+        useAccessibilityTimeout(false)
+
+        hum.showNotification(entry)
+        mSystemClock.advanceTime(
+            (TEST_TOUCH_ACCEPTANCE_TIME +
+                    (TEST_AUTO_DISMISS_TIME + TEST_A11Y_AUTO_DISMISS_TIME) / 2)
+                .toLong()
+        )
+
+        assertThat(hum.isHeadsUpEntry(entry.key)).isFalse()
+    }
+
+    @Test
+    fun testShowNotification_stickyForSomeTime_autoDismissesWithStickyTimeout() {
+        val hum = createHeadsUpManager()
+        val entry = createStickyForSomeTimeEntry(/* id= */ 0)
+        useAccessibilityTimeout(false)
+
+        hum.showNotification(entry)
+        mSystemClock.advanceTime(
+            (TEST_TOUCH_ACCEPTANCE_TIME +
+                    (TEST_AUTO_DISMISS_TIME + TEST_STICKY_AUTO_DISMISS_TIME) / 2)
+                .toLong()
+        )
+
+        assertThat(hum.isHeadsUpEntry(entry.key)).isTrue()
+    }
+
+    @Test
+    fun testShowNotification_sticky_neverAutoDismisses() {
+        val hum = createHeadsUpManager()
+        val entry = createStickyEntry(/* id= */ 0)
+        useAccessibilityTimeout(false)
+
+        hum.showNotification(entry)
+        mSystemClock.advanceTime(
+            (TEST_TOUCH_ACCEPTANCE_TIME + 2 * TEST_A11Y_AUTO_DISMISS_TIME).toLong()
+        )
+
+        assertThat(hum.isHeadsUpEntry(entry.key)).isTrue()
+    }
+
+    @Test
+    fun testShowNotification_autoDismissesWithAccessibilityTimeout() {
+        val hum = createHeadsUpManager()
+        val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+        useAccessibilityTimeout(true)
+
+        hum.showNotification(entry)
+        mSystemClock.advanceTime(
+            (TEST_TOUCH_ACCEPTANCE_TIME +
+                    (TEST_AUTO_DISMISS_TIME + TEST_A11Y_AUTO_DISMISS_TIME) / 2)
+                .toLong()
+        )
+
+        assertThat(hum.isHeadsUpEntry(entry.key)).isTrue()
+    }
+
+    @Test
+    fun testShowNotification_stickyForSomeTime_autoDismissesWithAccessibilityTimeout() {
+        val hum = createHeadsUpManager()
+        val entry = createStickyForSomeTimeEntry(/* id= */ 0)
+        useAccessibilityTimeout(true)
+
+        hum.showNotification(entry)
+        mSystemClock.advanceTime(
+            (TEST_TOUCH_ACCEPTANCE_TIME +
+                    (TEST_STICKY_AUTO_DISMISS_TIME + TEST_A11Y_AUTO_DISMISS_TIME) / 2)
+                .toLong()
+        )
+
+        assertThat(hum.isHeadsUpEntry(entry.key)).isTrue()
+    }
+
+    @Test
+    fun testRemoveNotification_beforeMinimumDisplayTime() {
+        val hum = createHeadsUpManager()
+        val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+        useAccessibilityTimeout(false)
+
+        hum.showNotification(entry)
+
+        val removedImmediately =
+            hum.removeNotification(
+                entry.key,
+                /* releaseImmediately = */ false,
+                "beforeMinimumDisplayTime",
+            )
+        assertThat(removedImmediately).isFalse()
+        assertThat(hum.isHeadsUpEntry(entry.key)).isTrue()
+
+        mSystemClock.advanceTime(
+            ((TEST_MINIMUM_DISPLAY_TIME + TEST_AUTO_DISMISS_TIME) / 2).toLong()
+        )
+
+        assertThat(hum.isHeadsUpEntry(entry.key)).isFalse()
+    }
+
+    @Test
+    fun testRemoveNotification_afterMinimumDisplayTime() {
+        val hum = createHeadsUpManager()
+        val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+        useAccessibilityTimeout(false)
+
+        hum.showNotification(entry)
+        mSystemClock.advanceTime(
+            ((TEST_MINIMUM_DISPLAY_TIME + TEST_AUTO_DISMISS_TIME) / 2).toLong()
+        )
+
+        assertThat(hum.isHeadsUpEntry(entry.key)).isTrue()
+
+        val removedImmediately =
+            hum.removeNotification(
+                entry.key,
+                /* releaseImmediately = */ false,
+                "afterMinimumDisplayTime",
+            )
+        assertThat(removedImmediately).isTrue()
+        assertThat(hum.isHeadsUpEntry(entry.key)).isFalse()
+    }
+
+    @Test
+    fun testRemoveNotification_releaseImmediately() {
+        val hum = createHeadsUpManager()
+        val entry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+        hum.showNotification(entry)
+
+        val removedImmediately =
+            hum.removeNotification(
+                entry.key,
+                /* releaseImmediately = */ true,
+                "afterMinimumDisplayTime",
+            )
+        assertThat(removedImmediately).isTrue()
+        assertThat(hum.isHeadsUpEntry(entry.key)).isFalse()
+    }
+
+    @Test
+    fun testIsSticky_rowPinnedAndExpanded_true() {
+        val hum = createHeadsUpManager()
+        val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+        Mockito.`when`(mRow!!.isPinned).thenReturn(true)
+        notifEntry.row = mRow
+
+        hum.showNotification(notifEntry)
+
+        val headsUpEntry = hum.getHeadsUpEntry(notifEntry.key)
+        headsUpEntry!!.setExpanded(true)
+
+        assertThat(hum.isSticky(notifEntry.key)).isTrue()
+    }
+
+    @Test
+    fun testIsSticky_remoteInputActive_true() {
+        val hum = createHeadsUpManager()
+        val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+        hum.showNotification(notifEntry)
+
+        val headsUpEntry = hum.getHeadsUpEntry(notifEntry.key)
+        headsUpEntry!!.mRemoteInputActive = true
+
+        assertThat(hum.isSticky(notifEntry.key)).isTrue()
+    }
+
+    @Test
+    fun testIsSticky_hasFullScreenIntent_true() {
+        val hum = createHeadsUpManager()
+        val notifEntry = HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id= */ 0, mContext)
+
+        hum.showNotification(notifEntry)
+
+        assertThat(hum.isSticky(notifEntry.key)).isTrue()
+    }
+
+    @Test
+    fun testIsSticky_stickyForSomeTime_false() {
+        val hum = createHeadsUpManager()
+        val entry = createStickyForSomeTimeEntry(/* id= */ 0)
+
+        hum.showNotification(entry)
+
+        assertThat(hum.isSticky(entry.key)).isFalse()
+    }
+
+    @Test
+    fun testIsSticky_false() {
+        val hum = createHeadsUpManager()
+        val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+        hum.showNotification(notifEntry)
+
+        val headsUpEntry = hum.getHeadsUpEntry(notifEntry.key)
+        headsUpEntry!!.setExpanded(false)
+        headsUpEntry.mRemoteInputActive = false
+
+        assertThat(hum.isSticky(notifEntry.key)).isFalse()
+    }
+
+    @Test
+    fun testCompareTo_withNullEntries() {
+        val hum = createHeadsUpManager()
+        val alertEntry = NotificationEntryBuilder().setTag("alert").build()
+
+        hum.showNotification(alertEntry)
+
+        assertThat(hum.compare(alertEntry, null)).isLessThan(0)
+        assertThat(hum.compare(null, alertEntry)).isGreaterThan(0)
+        assertThat(hum.compare(null, null)).isEqualTo(0)
+    }
+
+    @Test
+    fun testCompareTo_withNonAlertEntries() {
+        val hum = createHeadsUpManager()
+
+        val nonAlertEntry1 = NotificationEntryBuilder().setTag("nae1").build()
+        val nonAlertEntry2 = NotificationEntryBuilder().setTag("nae2").build()
+        val alertEntry = NotificationEntryBuilder().setTag("alert").build()
+        hum.showNotification(alertEntry)
+
+        assertThat(hum.compare(alertEntry, nonAlertEntry1)).isLessThan(0)
+        assertThat(hum.compare(nonAlertEntry1, alertEntry)).isGreaterThan(0)
+        assertThat(hum.compare(nonAlertEntry1, nonAlertEntry2)).isEqualTo(0)
+    }
+
+    @Test
+    fun testAlertEntryCompareTo_ongoingCallLessThanActiveRemoteInput() {
+        val hum = createHeadsUpManager()
+
+        val ongoingCall =
+            hum.HeadsUpEntry(
+                NotificationEntryBuilder()
+                    .setSbn(
+                        HeadsUpManagerTestUtil.createSbn(
+                            /* id = */ 0,
+                            Notification.Builder(mContext, "")
+                                .setCategory(Notification.CATEGORY_CALL)
+                                .setOngoing(true),
+                        )
+                    )
+                    .build()
+            )
+
+        val activeRemoteInput =
+            hum.HeadsUpEntry(HeadsUpManagerTestUtil.createEntry(/* id= */ 1, mContext))
+        activeRemoteInput.mRemoteInputActive = true
+
+        assertThat(ongoingCall.compareTo(activeRemoteInput)).isLessThan(0)
+        assertThat(activeRemoteInput.compareTo(ongoingCall)).isGreaterThan(0)
+    }
+
+    @Test
+    fun testAlertEntryCompareTo_incomingCallLessThanActiveRemoteInput() {
+        val hum = createHeadsUpManager()
+
+        val person = Person.Builder().setName("person").build()
+        val intent = Mockito.mock(PendingIntent::class.java)
+        val incomingCall =
+            hum.HeadsUpEntry(
+                NotificationEntryBuilder()
+                    .setSbn(
+                        HeadsUpManagerTestUtil.createSbn(
+                            /* id = */ 0,
+                            Notification.Builder(mContext, "")
+                                .setStyle(
+                                    Notification.CallStyle.forIncomingCall(person, intent, intent)
+                                ),
+                        )
+                    )
+                    .build()
+            )
+
+        val activeRemoteInput =
+            hum.HeadsUpEntry(HeadsUpManagerTestUtil.createEntry(/* id= */ 1, mContext))
+        activeRemoteInput.mRemoteInputActive = true
+
+        assertThat(incomingCall.compareTo(activeRemoteInput)).isLessThan(0)
+        assertThat(activeRemoteInput.compareTo(incomingCall)).isGreaterThan(0)
+    }
+
+    @Test
+    @EnableFlags(NotificationThrottleHun.FLAG_NAME)
+    fun testPinEntry_logsPeek_throttleEnabled() {
+        val hum = createHeadsUpManager()
+
+        // Needs full screen intent in order to be pinned
+        val entryToPin =
+            hum.HeadsUpEntry(
+                HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id= */ 0, mContext)
+            )
+
+        // Note: the standard way to show a notification would be calling showNotification rather
+        // than onAlertEntryAdded. However, in practice showNotification in effect adds
+        // the notification and then updates it; in order to not log twice, the entry needs
+        // to have a functional ExpandableNotificationRow that can keep track of whether it's
+        // pinned or not (via isRowPinned()). That feels like a lot to pull in to test this one bit.
+        hum.onEntryAdded(entryToPin)
+
+        assertThat(mUiEventLoggerFake.numLogs()).isEqualTo(2)
+        assertThat(
+            AvalancheController.ThrottleEvent.AVALANCHE_THROTTLING_HUN_SHOWN.getId(),
+        ).isEqualTo(mUiEventLoggerFake.eventId(0))
+        assertThat(
+            HeadsUpManagerImpl.NotificationPeekEvent.NOTIFICATION_PEEK.id,
+        ).isEqualTo(mUiEventLoggerFake.eventId(1))
+    }
+
+    @Test
+    @DisableFlags(NotificationThrottleHun.FLAG_NAME)
+    fun testPinEntry_logsPeek_throttleDisabled() {
+        val hum = createHeadsUpManager()
+
+        // Needs full screen intent in order to be pinned
+        val entryToPin =
+            hum.HeadsUpEntry(
+                HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id= */ 0, mContext)
+            )
+
+        // Note: the standard way to show a notification would be calling showNotification rather
+        // than onAlertEntryAdded. However, in practice showNotification in effect adds
+        // the notification and then updates it; in order to not log twice, the entry needs
+        // to have a functional ExpandableNotificationRow that can keep track of whether it's
+        // pinned or not (via isRowPinned()). That feels like a lot to pull in to test this one bit.
+        hum.onEntryAdded(entryToPin)
+
+        assertThat(mUiEventLoggerFake.numLogs()).isEqualTo(1)
+        assertThat(
+            HeadsUpManagerImpl.NotificationPeekEvent.NOTIFICATION_PEEK.id,
+        ).isEqualTo(mUiEventLoggerFake.eventId(0))
+    }
+
+    @Test
+    fun testSetUserActionMayIndirectlyRemove() {
+        val hum = createHeadsUpManager()
+        val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext)
+
+        hum.showNotification(notifEntry)
+
+        assertThat(hum.canRemoveImmediately(notifEntry.key)).isFalse()
+
+        hum.setUserActionMayIndirectlyRemove(notifEntry)
+
+        assertThat(hum.canRemoveImmediately(notifEntry.key)).isTrue()
+    }
+
+    companion object {
+        const val TEST_TOUCH_ACCEPTANCE_TIME: Int = 200
+        const val TEST_A11Y_AUTO_DISMISS_TIME: Int = 1000
+
+        const val TEST_MINIMUM_DISPLAY_TIME: Int = 400
+        const val TEST_AUTO_DISMISS_TIME: Int = 600
+        const val TEST_STICKY_AUTO_DISMISS_TIME: Int = 800
+
+        // Number of notifications to use in tests requiring multiple notifications
+        private const val TEST_NUM_NOTIFICATIONS = 4
+
+        init {
+            Truth.assertThat(TEST_MINIMUM_DISPLAY_TIME).isLessThan(TEST_AUTO_DISMISS_TIME)
+            Truth.assertThat(TEST_AUTO_DISMISS_TIME).isLessThan(TEST_STICKY_AUTO_DISMISS_TIME)
+            Truth.assertThat(TEST_STICKY_AUTO_DISMISS_TIME).isLessThan(TEST_A11Y_AUTO_DISMISS_TIME)
+        }
+
+        @get:Parameters(name = "{0}")
+        @JvmStatic
+        val flags: List<FlagsParameterization>
+            get() = FlagsParameterization.allCombinationsOf(NotificationThrottleHun.FLAG_NAME)
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplTest.java
deleted file mode 100644
index 01f78cb..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplTest.java
+++ /dev/null
@@ -1,664 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.headsup;
-
-import static android.app.Notification.FLAG_FSI_REQUESTED_BUT_DENIED;
-
-import static com.android.systemui.log.LogBufferHelperKt.logcatLogBuffer;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow;
-
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.app.Person;
-import android.os.Handler;
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.FlagsParameterization;
-import android.testing.TestableLooper;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.logging.testing.UiEventLoggerFake;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.kosmos.KosmosJavaAdapter;
-import com.android.systemui.res.R;
-import com.android.systemui.shade.domain.interactor.ShadeInteractor;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManagerImpl;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun;
-import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.kotlin.JavaAdapter;
-import com.android.systemui.util.settings.FakeGlobalSettings;
-import com.android.systemui.util.time.FakeSystemClock;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-
-import kotlinx.coroutines.flow.StateFlowKt;
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
-import platform.test.runner.parameterized.Parameters;
-
-import java.util.List;
-
-@SmallTest
-@TestableLooper.RunWithLooper
-@RunWith(ParameterizedAndroidJunit4.class)
-// TODO(b/378142453): Merge this with HeadsUpManagerPhoneTest.
-public class HeadsUpManagerImplTest extends SysuiTestCase {
-    protected KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
-
-    @Rule
-    public MockitoRule rule = MockitoJUnit.rule();
-
-    static final int TEST_TOUCH_ACCEPTANCE_TIME = 200;
-    static final int TEST_A11Y_AUTO_DISMISS_TIME = 1_000;
-
-    private UiEventLoggerFake mUiEventLoggerFake = new UiEventLoggerFake();
-
-    private final HeadsUpManagerLogger mLogger = spy(new HeadsUpManagerLogger(logcatLogBuffer()));
-    @Mock private Handler mBgHandler;
-    @Mock private DumpManager dumpManager;
-    @Mock private ShadeInteractor mShadeInteractor;
-    private AvalancheController mAvalancheController;
-
-    @Mock private AccessibilityManagerWrapper mAccessibilityMgr;
-
-    protected static final int TEST_MINIMUM_DISPLAY_TIME = 400;
-    protected static final int TEST_AUTO_DISMISS_TIME = 600;
-    protected static final int TEST_STICKY_AUTO_DISMISS_TIME = 800;
-    // Number of notifications to use in tests requiring multiple notifications
-    private static final int TEST_NUM_NOTIFICATIONS = 4;
-
-    protected final FakeGlobalSettings mGlobalSettings = new FakeGlobalSettings();
-    protected final FakeSystemClock mSystemClock = new FakeSystemClock();
-    protected final FakeExecutor mExecutor = new FakeExecutor(mSystemClock);
-
-    @Mock protected ExpandableNotificationRow mRow;
-
-    static {
-        assertThat(TEST_MINIMUM_DISPLAY_TIME).isLessThan(TEST_AUTO_DISMISS_TIME);
-        assertThat(TEST_AUTO_DISMISS_TIME).isLessThan(TEST_STICKY_AUTO_DISMISS_TIME);
-        assertThat(TEST_STICKY_AUTO_DISMISS_TIME).isLessThan(TEST_A11Y_AUTO_DISMISS_TIME);
-    }
-
-    private HeadsUpManagerImpl createHeadsUpManager() {
-        return new TestableHeadsUpManager(
-                mContext,
-                mLogger,
-                mKosmos.getStatusBarStateController(),
-                mKosmos.getKeyguardBypassController(),
-                new GroupMembershipManagerImpl(),
-                mKosmos.getVisualStabilityProvider(),
-                mKosmos.getConfigurationController(),
-                mExecutor,
-                mGlobalSettings,
-                mSystemClock,
-                mAccessibilityMgr,
-                mUiEventLoggerFake,
-                new JavaAdapter(mKosmos.getTestScope()),
-                mShadeInteractor,
-                mAvalancheController);
-    }
-
-    private NotificationEntry createStickyEntry(int id) {
-        final Notification notif = new Notification.Builder(mContext, "")
-                .setSmallIcon(R.drawable.ic_person)
-                .setFullScreenIntent(mock(PendingIntent.class), /* highPriority */ true)
-                .build();
-        return HeadsUpManagerTestUtil.createEntry(id, notif);
-    }
-
-    private NotificationEntry createStickyForSomeTimeEntry(int id) {
-        final Notification notif = new Notification.Builder(mContext, "")
-                .setSmallIcon(R.drawable.ic_person)
-                .setFlag(FLAG_FSI_REQUESTED_BUT_DENIED, true)
-                .build();
-        return HeadsUpManagerTestUtil.createEntry(id, notif);
-    }
-
-    private void useAccessibilityTimeout(boolean use) {
-        if (use) {
-            doReturn(TEST_A11Y_AUTO_DISMISS_TIME).when(mAccessibilityMgr)
-                    .getRecommendedTimeoutMillis(anyInt(), anyInt());
-        } else {
-            when(mAccessibilityMgr.getRecommendedTimeoutMillis(anyInt(), anyInt())).then(
-                    i -> i.getArgument(0));
-        }
-    }
-
-    @Parameters(name = "{0}")
-    public static List<FlagsParameterization> getFlags() {
-        return FlagsParameterization.allCombinationsOf(NotificationThrottleHun.FLAG_NAME);
-    }
-
-    public HeadsUpManagerImplTest(FlagsParameterization flags) {
-        mSetFlagsRule.setFlagsParameterization(flags);
-    }
-
-    @Override
-    public void SysuiSetup() throws Exception {
-        super.SysuiSetup();
-        mAvalancheController = new AvalancheController(dumpManager, mUiEventLoggerFake, mLogger,
-                mBgHandler);
-        when(mShadeInteractor.isAnyExpanded()).thenReturn(MutableStateFlow(true));
-        when(mKosmos.getKeyguardBypassController().getBypassEnabled()).thenReturn(false);
-    }
-
-    @Test
-    public void testHasNotifications_headsUpManagerMapNotEmpty_true() {
-        final HeadsUpManagerImpl bhum = createHeadsUpManager();
-        final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
-        bhum.showNotification(entry);
-
-        assertThat(bhum.mHeadsUpEntryMap).isNotEmpty();
-        assertThat(bhum.hasNotifications()).isTrue();
-    }
-
-    @Test
-    @EnableFlags(NotificationThrottleHun.FLAG_NAME)
-    public void testHasNotifications_avalancheMapNotEmpty_true() {
-        final HeadsUpManagerImpl bhum = createHeadsUpManager();
-        final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0,
-                mContext);
-        final HeadsUpManagerImpl.HeadsUpEntry headsUpEntry = bhum.createHeadsUpEntry(notifEntry);
-        mAvalancheController.addToNext(headsUpEntry, () -> {});
-
-        assertThat(mAvalancheController.getWaitingEntryList()).isNotEmpty();
-        assertThat(bhum.hasNotifications()).isTrue();
-    }
-
-    @Test
-    @EnableFlags(NotificationThrottleHun.FLAG_NAME)
-    public void testHasNotifications_false() {
-        final HeadsUpManagerImpl bhum = createHeadsUpManager();
-        assertThat(bhum.mHeadsUpEntryMap).isEmpty();
-        assertThat(mAvalancheController.getWaitingEntryList()).isEmpty();
-        assertThat(bhum.hasNotifications()).isFalse();
-    }
-
-    @Test
-    @EnableFlags(NotificationThrottleHun.FLAG_NAME)
-    public void testGetHeadsUpEntryList_includesAvalancheEntryList() {
-        final HeadsUpManagerImpl bhum = createHeadsUpManager();
-        final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0,
-                mContext);
-        final HeadsUpManagerImpl.HeadsUpEntry headsUpEntry = bhum.createHeadsUpEntry(notifEntry);
-        mAvalancheController.addToNext(headsUpEntry, () -> {});
-
-        assertThat(bhum.getHeadsUpEntryList()).contains(headsUpEntry);
-    }
-
-    @Test
-    @EnableFlags(NotificationThrottleHun.FLAG_NAME)
-    public void testGetHeadsUpEntry_returnsAvalancheEntry() {
-        final HeadsUpManagerImpl bhum = createHeadsUpManager();
-        final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0,
-                mContext);
-        final HeadsUpManagerImpl.HeadsUpEntry headsUpEntry = bhum.createHeadsUpEntry(notifEntry);
-        mAvalancheController.addToNext(headsUpEntry, () -> {});
-
-        assertThat(bhum.getHeadsUpEntry(notifEntry.getKey())).isEqualTo(headsUpEntry);
-    }
-
-    @Test
-    public void testShowNotification_addsEntry() {
-        final HeadsUpManagerImpl alm = createHeadsUpManager();
-        final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
-
-        alm.showNotification(entry);
-
-        assertTrue(alm.isHeadsUpEntry(entry.getKey()));
-        assertTrue(alm.hasNotifications());
-        assertEquals(entry, alm.getEntry(entry.getKey()));
-    }
-
-    @Test
-    public void testShowNotification_autoDismisses() {
-        final HeadsUpManagerImpl alm = createHeadsUpManager();
-        final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
-
-        alm.showNotification(entry);
-        mSystemClock.advanceTime(TEST_AUTO_DISMISS_TIME * 3 / 2);
-
-        assertFalse(alm.isHeadsUpEntry(entry.getKey()));
-    }
-
-    @Test
-    public void testRemoveNotification_removeDeferred() {
-        final HeadsUpManagerImpl alm = createHeadsUpManager();
-        final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
-
-        alm.showNotification(entry);
-
-        final boolean removedImmediately = alm.removeNotification(
-                entry.getKey(), /* releaseImmediately = */ false, "removeDeferred");
-        assertFalse(removedImmediately);
-        assertTrue(alm.isHeadsUpEntry(entry.getKey()));
-    }
-
-    @Test
-    public void testRemoveNotification_forceRemove() {
-        final HeadsUpManagerImpl alm = createHeadsUpManager();
-        final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
-
-        alm.showNotification(entry);
-
-        final boolean removedImmediately = alm.removeNotification(
-                entry.getKey(), /* releaseImmediately = */ true, "forceRemove");
-        assertTrue(removedImmediately);
-        assertFalse(alm.isHeadsUpEntry(entry.getKey()));
-    }
-
-    @Test
-    public void testReleaseAllImmediately() {
-        final HeadsUpManagerImpl alm = createHeadsUpManager();
-        for (int i = 0; i < TEST_NUM_NOTIFICATIONS; i++) {
-            final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(i, mContext);
-            entry.setRow(mRow);
-            alm.showNotification(entry);
-        }
-
-        alm.releaseAllImmediately();
-
-        assertEquals(0, alm.getAllEntries().count());
-    }
-
-    @Test
-    public void testCanRemoveImmediately_notShownLongEnough() {
-        final HeadsUpManagerImpl alm = createHeadsUpManager();
-        final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
-
-        alm.showNotification(entry);
-
-        // The entry has just been added so we should not remove immediately.
-        assertFalse(alm.canRemoveImmediately(entry.getKey()));
-    }
-
-    @Test
-    public void testHunRemovedLogging() {
-        final HeadsUpManagerImpl hum = createHeadsUpManager();
-        final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0,
-                mContext);
-        final HeadsUpManagerImpl.HeadsUpEntry headsUpEntry = mock(
-                HeadsUpManagerImpl.HeadsUpEntry.class);
-        when(headsUpEntry.getPinnedStatus())
-                .thenReturn(StateFlowKt.MutableStateFlow(PinnedStatus.NotPinned));
-        headsUpEntry.mEntry = notifEntry;
-
-        hum.onEntryRemoved(headsUpEntry, "test");
-
-        verify(mLogger, times(1)).logNotificationActuallyRemoved(eq(notifEntry));
-    }
-
-
-    @Test
-    public void testShowNotification_autoDismissesIncludingTouchAcceptanceDelay() {
-        final HeadsUpManagerImpl hum = createHeadsUpManager();
-        final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
-        useAccessibilityTimeout(false);
-
-        hum.showNotification(entry);
-        mSystemClock.advanceTime(TEST_TOUCH_ACCEPTANCE_TIME / 2 + TEST_AUTO_DISMISS_TIME);
-
-        assertTrue(hum.isHeadsUpEntry(entry.getKey()));
-    }
-
-
-    @Test
-    public void testShowNotification_autoDismissesWithDefaultTimeout() {
-        final HeadsUpManagerImpl hum = createHeadsUpManager();
-        final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
-        useAccessibilityTimeout(false);
-
-        hum.showNotification(entry);
-        mSystemClock.advanceTime(TEST_TOUCH_ACCEPTANCE_TIME
-                + (TEST_AUTO_DISMISS_TIME + TEST_A11Y_AUTO_DISMISS_TIME) / 2);
-
-        assertFalse(hum.isHeadsUpEntry(entry.getKey()));
-    }
-
-
-    @Test
-    public void testShowNotification_stickyForSomeTime_autoDismissesWithStickyTimeout() {
-        final HeadsUpManagerImpl hum = createHeadsUpManager();
-        final NotificationEntry entry = createStickyForSomeTimeEntry(/* id = */ 0);
-        useAccessibilityTimeout(false);
-
-        hum.showNotification(entry);
-        mSystemClock.advanceTime(TEST_TOUCH_ACCEPTANCE_TIME
-                + (TEST_AUTO_DISMISS_TIME + TEST_STICKY_AUTO_DISMISS_TIME) / 2);
-
-        assertTrue(hum.isHeadsUpEntry(entry.getKey()));
-    }
-
-
-    @Test
-    public void testShowNotification_sticky_neverAutoDismisses() {
-        final HeadsUpManagerImpl hum = createHeadsUpManager();
-        final NotificationEntry entry = createStickyEntry(/* id = */ 0);
-        useAccessibilityTimeout(false);
-
-        hum.showNotification(entry);
-        mSystemClock.advanceTime(TEST_TOUCH_ACCEPTANCE_TIME + 2 * TEST_A11Y_AUTO_DISMISS_TIME);
-
-        assertTrue(hum.isHeadsUpEntry(entry.getKey()));
-    }
-
-
-    @Test
-    public void testShowNotification_autoDismissesWithAccessibilityTimeout() {
-        final HeadsUpManagerImpl hum = createHeadsUpManager();
-        final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
-        useAccessibilityTimeout(true);
-
-        hum.showNotification(entry);
-        mSystemClock.advanceTime(TEST_TOUCH_ACCEPTANCE_TIME
-                + (TEST_AUTO_DISMISS_TIME + TEST_A11Y_AUTO_DISMISS_TIME) / 2);
-
-        assertTrue(hum.isHeadsUpEntry(entry.getKey()));
-    }
-
-
-    @Test
-    public void testShowNotification_stickyForSomeTime_autoDismissesWithAccessibilityTimeout() {
-        final HeadsUpManagerImpl hum = createHeadsUpManager();
-        final NotificationEntry entry = createStickyForSomeTimeEntry(/* id = */ 0);
-        useAccessibilityTimeout(true);
-
-        hum.showNotification(entry);
-        mSystemClock.advanceTime(TEST_TOUCH_ACCEPTANCE_TIME
-                + (TEST_STICKY_AUTO_DISMISS_TIME + TEST_A11Y_AUTO_DISMISS_TIME) / 2);
-
-        assertTrue(hum.isHeadsUpEntry(entry.getKey()));
-    }
-
-
-    @Test
-    public void testRemoveNotification_beforeMinimumDisplayTime() {
-        final HeadsUpManagerImpl hum = createHeadsUpManager();
-        final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
-        useAccessibilityTimeout(false);
-
-        hum.showNotification(entry);
-
-        final boolean removedImmediately = hum.removeNotification(
-                entry.getKey(), /* releaseImmediately = */ false, "beforeMinimumDisplayTime");
-        assertFalse(removedImmediately);
-        assertTrue(hum.isHeadsUpEntry(entry.getKey()));
-
-        mSystemClock.advanceTime((TEST_MINIMUM_DISPLAY_TIME + TEST_AUTO_DISMISS_TIME) / 2);
-
-        assertFalse(hum.isHeadsUpEntry(entry.getKey()));
-    }
-
-
-    @Test
-    public void testRemoveNotification_afterMinimumDisplayTime() {
-        final HeadsUpManagerImpl hum = createHeadsUpManager();
-        final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
-        useAccessibilityTimeout(false);
-
-        hum.showNotification(entry);
-        mSystemClock.advanceTime((TEST_MINIMUM_DISPLAY_TIME + TEST_AUTO_DISMISS_TIME) / 2);
-
-        assertTrue(hum.isHeadsUpEntry(entry.getKey()));
-
-        final boolean removedImmediately = hum.removeNotification(
-                entry.getKey(), /* releaseImmediately = */ false, "afterMinimumDisplayTime");
-        assertTrue(removedImmediately);
-        assertFalse(hum.isHeadsUpEntry(entry.getKey()));
-    }
-
-
-    @Test
-    public void testRemoveNotification_releaseImmediately() {
-        final HeadsUpManagerImpl hum = createHeadsUpManager();
-        final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
-
-        hum.showNotification(entry);
-
-        final boolean removedImmediately = hum.removeNotification(
-                entry.getKey(), /* releaseImmediately = */ true, "afterMinimumDisplayTime");
-        assertTrue(removedImmediately);
-        assertFalse(hum.isHeadsUpEntry(entry.getKey()));
-    }
-
-
-    @Test
-    public void testIsSticky_rowPinnedAndExpanded_true() {
-        final HeadsUpManagerImpl hum = createHeadsUpManager();
-        final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0,
-                mContext);
-        when(mRow.isPinned()).thenReturn(true);
-        notifEntry.setRow(mRow);
-
-        hum.showNotification(notifEntry);
-
-        final HeadsUpManagerImpl.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry(
-                notifEntry.getKey());
-        headsUpEntry.setExpanded(true);
-
-        assertTrue(hum.isSticky(notifEntry.getKey()));
-    }
-
-    @Test
-    public void testIsSticky_remoteInputActive_true() {
-        final HeadsUpManagerImpl hum = createHeadsUpManager();
-        final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0,
-                mContext);
-
-        hum.showNotification(notifEntry);
-
-        final HeadsUpManagerImpl.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry(
-                notifEntry.getKey());
-        headsUpEntry.mRemoteInputActive = true;
-
-        assertTrue(hum.isSticky(notifEntry.getKey()));
-    }
-
-    @Test
-    public void testIsSticky_hasFullScreenIntent_true() {
-        final HeadsUpManagerImpl hum = createHeadsUpManager();
-        final NotificationEntry notifEntry =
-                HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id = */ 0, mContext);
-
-        hum.showNotification(notifEntry);
-
-        assertTrue(hum.isSticky(notifEntry.getKey()));
-    }
-
-
-    @Test
-    public void testIsSticky_stickyForSomeTime_false() {
-        final HeadsUpManagerImpl hum = createHeadsUpManager();
-        final NotificationEntry entry = createStickyForSomeTimeEntry(/* id = */ 0);
-
-        hum.showNotification(entry);
-
-        assertFalse(hum.isSticky(entry.getKey()));
-    }
-
-
-    @Test
-    public void testIsSticky_false() {
-        final HeadsUpManagerImpl hum = createHeadsUpManager();
-        final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0,
-                mContext);
-
-        hum.showNotification(notifEntry);
-
-        final HeadsUpManagerImpl.HeadsUpEntry headsUpEntry = hum.getHeadsUpEntry(
-                notifEntry.getKey());
-        headsUpEntry.setExpanded(false);
-        headsUpEntry.mRemoteInputActive = false;
-
-        assertFalse(hum.isSticky(notifEntry.getKey()));
-    }
-
-    @Test
-    public void testCompareTo_withNullEntries() {
-        final HeadsUpManagerImpl hum = createHeadsUpManager();
-        final NotificationEntry alertEntry = new NotificationEntryBuilder().setTag("alert").build();
-
-        hum.showNotification(alertEntry);
-
-        assertThat(hum.compare(alertEntry, null)).isLessThan(0);
-        assertThat(hum.compare(null, alertEntry)).isGreaterThan(0);
-        assertThat(hum.compare(null, null)).isEqualTo(0);
-    }
-
-    @Test
-    public void testCompareTo_withNonAlertEntries() {
-        final HeadsUpManagerImpl hum = createHeadsUpManager();
-
-        final NotificationEntry nonAlertEntry1 = new NotificationEntryBuilder().setTag(
-                "nae1").build();
-        final NotificationEntry nonAlertEntry2 = new NotificationEntryBuilder().setTag(
-                "nae2").build();
-        final NotificationEntry alertEntry = new NotificationEntryBuilder().setTag("alert").build();
-        hum.showNotification(alertEntry);
-
-        assertThat(hum.compare(alertEntry, nonAlertEntry1)).isLessThan(0);
-        assertThat(hum.compare(nonAlertEntry1, alertEntry)).isGreaterThan(0);
-        assertThat(hum.compare(nonAlertEntry1, nonAlertEntry2)).isEqualTo(0);
-    }
-
-    @Test
-    public void testAlertEntryCompareTo_ongoingCallLessThanActiveRemoteInput() {
-        final HeadsUpManagerImpl hum = createHeadsUpManager();
-
-        final HeadsUpManagerImpl.HeadsUpEntry ongoingCall = hum.new HeadsUpEntry(
-                new NotificationEntryBuilder()
-                        .setSbn(HeadsUpManagerTestUtil.createSbn(/* id = */ 0,
-                                new Notification.Builder(mContext, "")
-                                        .setCategory(Notification.CATEGORY_CALL)
-                                        .setOngoing(true)))
-                        .build());
-
-        final HeadsUpManagerImpl.HeadsUpEntry activeRemoteInput = hum.new HeadsUpEntry(
-                HeadsUpManagerTestUtil.createEntry(/* id = */ 1, mContext));
-        activeRemoteInput.mRemoteInputActive = true;
-
-        assertThat(ongoingCall.compareTo(activeRemoteInput)).isLessThan(0);
-        assertThat(activeRemoteInput.compareTo(ongoingCall)).isGreaterThan(0);
-    }
-
-    @Test
-    public void testAlertEntryCompareTo_incomingCallLessThanActiveRemoteInput() {
-        final HeadsUpManagerImpl hum = createHeadsUpManager();
-
-        final Person person = new Person.Builder().setName("person").build();
-        final PendingIntent intent = mock(PendingIntent.class);
-        final HeadsUpManagerImpl.HeadsUpEntry incomingCall = hum.new HeadsUpEntry(
-                new NotificationEntryBuilder()
-                        .setSbn(HeadsUpManagerTestUtil.createSbn(/* id = */ 0,
-                                new Notification.Builder(mContext, "")
-                                        .setStyle(Notification.CallStyle
-                                                .forIncomingCall(person, intent, intent))))
-                        .build());
-
-        final HeadsUpManagerImpl.HeadsUpEntry activeRemoteInput = hum.new HeadsUpEntry(
-                HeadsUpManagerTestUtil.createEntry(/* id = */ 1, mContext));
-        activeRemoteInput.mRemoteInputActive = true;
-
-        assertThat(incomingCall.compareTo(activeRemoteInput)).isLessThan(0);
-        assertThat(activeRemoteInput.compareTo(incomingCall)).isGreaterThan(0);
-    }
-
-    @Test
-    @EnableFlags(NotificationThrottleHun.FLAG_NAME)
-    public void testPinEntry_logsPeek_throttleEnabled() {
-        final HeadsUpManagerImpl hum = createHeadsUpManager();
-
-        // Needs full screen intent in order to be pinned
-        final HeadsUpManagerImpl.HeadsUpEntry entryToPin = hum.new HeadsUpEntry(
-                HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id = */ 0, mContext));
-
-        // Note: the standard way to show a notification would be calling showNotification rather
-        // than onAlertEntryAdded. However, in practice showNotification in effect adds
-        // the notification and then updates it; in order to not log twice, the entry needs
-        // to have a functional ExpandableNotificationRow that can keep track of whether it's
-        // pinned or not (via isRowPinned()). That feels like a lot to pull in to test this one bit.
-        hum.onEntryAdded(entryToPin);
-
-        assertEquals(2, mUiEventLoggerFake.numLogs());
-        assertEquals(AvalancheController.ThrottleEvent.AVALANCHE_THROTTLING_HUN_SHOWN.getId(),
-                mUiEventLoggerFake.eventId(0));
-        assertEquals(HeadsUpManagerImpl.NotificationPeekEvent.NOTIFICATION_PEEK.getId(),
-                mUiEventLoggerFake.eventId(1));
-    }
-
-    @Test
-    @DisableFlags(NotificationThrottleHun.FLAG_NAME)
-    public void testPinEntry_logsPeek_throttleDisabled() {
-        final HeadsUpManagerImpl hum = createHeadsUpManager();
-
-        // Needs full screen intent in order to be pinned
-        final HeadsUpManagerImpl.HeadsUpEntry entryToPin = hum.new HeadsUpEntry(
-                HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id = */ 0, mContext));
-
-        // Note: the standard way to show a notification would be calling showNotification rather
-        // than onAlertEntryAdded. However, in practice showNotification in effect adds
-        // the notification and then updates it; in order to not log twice, the entry needs
-        // to have a functional ExpandableNotificationRow that can keep track of whether it's
-        // pinned or not (via isRowPinned()). That feels like a lot to pull in to test this one bit.
-        hum.onEntryAdded(entryToPin);
-
-        assertEquals(1, mUiEventLoggerFake.numLogs());
-        assertEquals(HeadsUpManagerImpl.NotificationPeekEvent.NOTIFICATION_PEEK.getId(),
-                mUiEventLoggerFake.eventId(0));
-    }
-
-    @Test
-    public void testSetUserActionMayIndirectlyRemove() {
-        final HeadsUpManagerImpl hum = createHeadsUpManager();
-        final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0,
-                mContext);
-
-        hum.showNotification(notifEntry);
-
-        assertFalse(hum.canRemoveImmediately(notifEntry.getKey()));
-
-        hum.setUserActionMayIndirectlyRemove(notifEntry);
-
-        assertTrue(hum.canRemoveImmediately(notifEntry.getKey()));
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerPhoneTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplTest.kt
similarity index 98%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerPhoneTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplTest.kt
index 35d8253..65d282f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerPhoneTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerImplTest.kt
@@ -59,7 +59,7 @@
 @SmallTest
 @RunWith(ParameterizedAndroidJunit4::class)
 @RunWithLooper
-class HeadsUpManagerPhoneTest(flags: FlagsParameterization) : HeadsUpManagerImplTest(flags) {
+class HeadsUpManagerImplTest(flags: FlagsParameterization) : HeadsUpManagerImplOldTest(flags) {
 
     private val mHeadsUpManagerLogger = HeadsUpManagerLogger(logcatLogBuffer())
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/TestableHeadsUpManager.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/TestableHeadsUpManager.java
index 2b077ed..7ded1a5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/TestableHeadsUpManager.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/TestableHeadsUpManager.java
@@ -78,10 +78,10 @@
                 shadeInteractor,
                 avalancheController);
 
-        mTouchAcceptanceDelay = HeadsUpManagerImplTest.TEST_TOUCH_ACCEPTANCE_TIME;
-        mMinimumDisplayTime = HeadsUpManagerImplTest.TEST_MINIMUM_DISPLAY_TIME;
-        mAutoDismissTime = HeadsUpManagerImplTest.TEST_AUTO_DISMISS_TIME;
-        mStickyForSomeTimeAutoDismissTime = HeadsUpManagerImplTest.TEST_STICKY_AUTO_DISMISS_TIME;
+        mTouchAcceptanceDelay = HeadsUpManagerImplOldTest.TEST_TOUCH_ACCEPTANCE_TIME;
+        mMinimumDisplayTime = HeadsUpManagerImplOldTest.TEST_MINIMUM_DISPLAY_TIME;
+        mAutoDismissTime = HeadsUpManagerImplOldTest.TEST_AUTO_DISMISS_TIME;
+        mStickyForSomeTimeAutoDismissTime = HeadsUpManagerImplOldTest.TEST_STICKY_AUTO_DISMISS_TIME;
     }
 
     @NonNull
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogSafetyWarningInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogSafetyWarningInteractorTest.kt
new file mode 100644
index 0000000..8acf538
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogSafetyWarningInteractorTest.kt
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.domain.interactor
+
+import android.app.ActivityManager
+import android.media.AudioManager
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.plugins.fakeVolumeDialogController
+import com.android.systemui.testKosmos
+import com.android.systemui.volume.Events
+import com.android.systemui.volume.dialog.data.repository.volumeDialogVisibilityRepository
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@TestableLooper.RunWithLooper
+class VolumeDialogSafetyWarningInteractorTest : SysuiTestCase() {
+
+    private val kosmos: Kosmos = testKosmos()
+
+    private lateinit var underTest: VolumeDialogSafetyWarningInteractor
+
+    @Before
+    fun setup() {
+        kosmos.useUnconfinedTestDispatcher()
+        underTest = kosmos.volumeDialogSafetyWarningInteractor
+    }
+
+    @Test
+    fun dismiss_isShowingSafetyWarning_isFalse() =
+        with(kosmos) {
+            runTest {
+                val isShowingSafetyWarning by collectLastValue(underTest.isShowingSafetyWarning)
+
+                underTest.onSafetyWarningDismissed()
+
+                assertThat(isShowingSafetyWarning).isFalse()
+            }
+        }
+
+    @Test
+    fun flagShowUi_isShowingSafetyWarning_isTrue() =
+        with(kosmos) {
+            runTest {
+                val isShowingSafetyWarning by collectLastValue(underTest.isShowingSafetyWarning)
+
+                fakeVolumeDialogController.onShowSafetyWarning(AudioManager.FLAG_SHOW_UI)
+
+                assertThat(isShowingSafetyWarning).isTrue()
+            }
+        }
+
+    @Test
+    fun flagShowUiWarnings_isShowingSafetyWarning_isTrue() =
+        with(kosmos) {
+            runTest {
+                val isShowingSafetyWarning by collectLastValue(underTest.isShowingSafetyWarning)
+
+                fakeVolumeDialogController.onShowSafetyWarning(AudioManager.FLAG_SHOW_UI_WARNINGS)
+
+                assertThat(isShowingSafetyWarning).isTrue()
+            }
+        }
+
+    @Test
+    fun invisibleAndNoFlags_isShowingSafetyWarning_isFalse() =
+        with(kosmos) {
+            runTest {
+                val isShowingSafetyWarning by collectLastValue(underTest.isShowingSafetyWarning)
+                volumeDialogVisibilityRepository.updateVisibility {
+                    VolumeDialogVisibilityModel.Invisible
+                }
+
+                fakeVolumeDialogController.onShowSafetyWarning(0)
+
+                assertThat(isShowingSafetyWarning).isFalse()
+            }
+        }
+
+    @Test
+    fun visibleAndNoFlags_isShowingSafetyWarning_isTrue() =
+        with(kosmos) {
+            runTest {
+                val isShowingSafetyWarning by collectLastValue(underTest.isShowingSafetyWarning)
+                volumeDialogVisibilityRepository.updateVisibility {
+                    VolumeDialogVisibilityModel.Visible(
+                        Events.SHOW_REASON_VOLUME_CHANGED,
+                        false,
+                        ActivityManager.LOCK_TASK_MODE_LOCKED,
+                    )
+                }
+
+                fakeVolumeDialogController.onShowSafetyWarning(0)
+
+                assertThat(isShowingSafetyWarning).isTrue()
+            }
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/EmergencyServicesRepository.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/EmergencyServicesRepository.kt
index bba0050..a42ae03 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/EmergencyServicesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/EmergencyServicesRepository.kt
@@ -18,6 +18,7 @@
 
 import android.content.res.Resources
 import com.android.internal.R
+import com.android.systemui.common.ui.GlobalConfig
 import com.android.systemui.common.ui.data.repository.ConfigurationRepository
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
@@ -36,7 +37,7 @@
 constructor(
     @Application private val applicationScope: CoroutineScope,
     @Main private val resources: Resources,
-    configurationRepository: ConfigurationRepository,
+    @GlobalConfig configurationRepository: ConfigurationRepository,
 ) {
     /**
      * Whether to enable emergency services calls while the SIM card is locked. This is disabled in
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
index 61cd7c7..641400a 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
@@ -42,6 +42,7 @@
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.res.R
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.shade.ShadeDisplayAware
 import com.android.systemui.shared.system.SysUiStatsLog
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
@@ -71,7 +72,7 @@
     private val primaryBouncerCallbackInteractor: PrimaryBouncerCallbackInteractor,
     private val falsingCollector: FalsingCollector,
     private val dismissCallbackRegistry: DismissCallbackRegistry,
-    private val context: Context,
+    @ShadeDisplayAware private val context: Context,
     private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
     private val trustRepository: TrustRepository,
     @Application private val applicationScope: CoroutineScope,
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractor.kt
index 1aaf4fb..ec9ee91 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractor.kt
@@ -37,6 +37,7 @@
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeDisplayAware
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
 import com.android.systemui.util.icuMessageFormat
 import javax.inject.Inject
@@ -62,7 +63,7 @@
     @Background private val backgroundDispatcher: CoroutineDispatcher,
     private val repository: SimBouncerRepository,
     private val telephonyManager: TelephonyManager,
-    @Main private val resources: Resources,
+    @ShadeDisplayAware private val resources: Resources,
     private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
     private val euiccManager: EuiccManager?,
     // TODO(b/307977401): Replace this with `MobileConnectionsInteractor` when available.
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt
index 38fc2a8..84a423e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt
@@ -21,11 +21,12 @@
 import android.view.WindowManager
 import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.settingslib.Utils
+import com.android.systemui.common.ui.GlobalConfig
 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.keyboard.docking.domain.interactor.KeyboardDockingIndicationInteractor
-import com.android.systemui.shade.ShadeDisplayAware
 import com.android.systemui.surfaceeffects.glowboxeffect.GlowBoxConfig
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
@@ -37,9 +38,9 @@
 @Inject
 constructor(
     private val windowManager: WindowManager,
-    private val context: Context,
+    @Application private val context: Context,
     keyboardDockingIndicationInteractor: KeyboardDockingIndicationInteractor,
-    @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
+    @GlobalConfig configurationInteractor: ConfigurationInteractor,
     @Background private val backgroundScope: CoroutineScope,
 ) {
 
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt
index 094ec39..203a157 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt
@@ -16,19 +16,31 @@
 
 package com.android.systemui.volume.dialog
 
+import android.content.Context
+import android.media.AudioManager
 import com.android.app.tracing.coroutines.coroutineScopeTraced
 import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.plugins.VolumeDialog
+import com.android.systemui.volume.SafetyWarningDialog
 import com.android.systemui.volume.dialog.dagger.VolumeDialogPluginComponent
+import com.android.systemui.volume.dialog.ui.viewmodel.VolumeDialogPluginViewModel
 import javax.inject.Inject
+import kotlin.coroutines.resume
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.mapLatest
+import kotlinx.coroutines.suspendCancellableCoroutine
 
+@OptIn(ExperimentalCoroutinesApi::class)
 class VolumeDialogPlugin
 @Inject
 constructor(
     @Application private val applicationCoroutineScope: CoroutineScope,
+    private val context: Context,
+    private val audioManager: AudioManager,
     private val volumeDialogPluginComponentFactory: VolumeDialogPluginComponent.Factory,
 ) : VolumeDialog {
 
@@ -41,14 +53,39 @@
                 coroutineScopeTraced("[Volume]plugin") {
                     pluginComponent =
                         volumeDialogPluginComponentFactory.create(this).also {
-                            it.viewModel().launchVolumeDialog()
+                            bindPlugin(it.viewModel())
                         }
                 }
             }
     }
 
+    private fun CoroutineScope.bindPlugin(viewModel: VolumeDialogPluginViewModel) {
+        viewModel.launchVolumeDialog()
+
+        viewModel.isShowingSafetyWarning
+            .mapLatest { isShowingSafetyWarning ->
+                if (isShowingSafetyWarning) {
+                    showSafetyWarningVisibility { viewModel.onSafetyWarningDismissed() }
+                }
+            }
+            .launchIn(this)
+    }
+
     override fun destroy() {
         job?.cancel()
         pluginComponent = null
     }
+
+    private suspend fun showSafetyWarningVisibility(onDismissed: () -> Unit) =
+        suspendCancellableCoroutine { continuation ->
+            val dialog =
+                object : SafetyWarningDialog(context, audioManager) {
+                    override fun cleanUp() {
+                        onDismissed()
+                        continuation.resume(Unit)
+                    }
+                }
+            dialog.show()
+            continuation.invokeOnCancellation { dialog.dismiss() }
+        }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogSafetyWarningInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogSafetyWarningInteractor.kt
new file mode 100644
index 0000000..f707d67
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogSafetyWarningInteractor.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.domain.interactor
+
+import android.media.AudioManager
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPluginScope
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogSafetyWarningModel
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.map
+
+private const val VISIBLE_FLAGS = AudioManager.FLAG_SHOW_UI or AudioManager.FLAG_SHOW_UI_WARNINGS
+
+@VolumeDialogPluginScope
+class VolumeDialogSafetyWarningInteractor
+@Inject
+constructor(
+    private val stateInteractor: VolumeDialogStateInteractor,
+    visibilityInteractor: VolumeDialogVisibilityInteractor,
+) {
+
+    val isShowingSafetyWarning: Flow<Boolean> =
+        stateInteractor.volumeDialogState.map {
+            when (it.isShowingSafetyWarning) {
+                is VolumeDialogSafetyWarningModel.Visible ->
+                    if (it.isShowingSafetyWarning.flags and VISIBLE_FLAGS == 0) {
+                        visibilityInteractor.dialogVisibility.first() is
+                            VolumeDialogVisibilityModel.Visible
+                    } else {
+                        true
+                    }
+                is VolumeDialogSafetyWarningModel.Invisible -> false
+            }
+        }
+
+    fun onSafetyWarningDismissed() {
+        stateInteractor.setSafetyWarning(VolumeDialogSafetyWarningModel.Invisible)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogStateInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogStateInteractor.kt
index 5c7289b..51e7924 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogStateInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogStateInteractor.kt
@@ -23,6 +23,7 @@
 import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPluginScope
 import com.android.systemui.volume.dialog.data.repository.VolumeDialogStateRepository
 import com.android.systemui.volume.dialog.domain.model.VolumeDialogEventModel
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogSafetyWarningModel
 import com.android.systemui.volume.dialog.shared.model.VolumeDialogStateModel
 import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
 import javax.inject.Inject
@@ -61,6 +62,9 @@
                             oldState.copy(shouldShowA11ySlider = event.showA11yStream)
                         }
                     }
+                    is VolumeDialogEventModel.ShowSafetyWarning -> {
+                        setSafetyWarning(VolumeDialogSafetyWarningModel.Visible(event.flags))
+                    }
                     else -> {
                         // do nothing
                     }
@@ -72,6 +76,10 @@
 
     val volumeDialogState: Flow<VolumeDialogStateModel> = volumeDialogStateRepository.state
 
+    fun setSafetyWarning(model: VolumeDialogSafetyWarningModel) {
+        volumeDialogStateRepository.updateState { it.copy(isShowingSafetyWarning = model) }
+    }
+
     /** Returns a copy of [model] filled with the values from [VolumeDialogController.State]. */
     private fun VolumeDialogController.State.copyIntoModel(
         model: VolumeDialogStateModel
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogSafetyWarningModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogSafetyWarningModel.kt
new file mode 100644
index 0000000..39fc222
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogSafetyWarningModel.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.shared.model
+
+/** Models current Volume Safety Dialog state. */
+sealed interface VolumeDialogSafetyWarningModel {
+
+    /** Volume Safety Dialog is visible and has been shown with the [flags]. */
+    data class Visible(val flags: Int) : VolumeDialogSafetyWarningModel
+
+    /** Volume Safety Dialog is invisible. */
+    data object Invisible : VolumeDialogSafetyWarningModel
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogStateModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogStateModel.kt
index 1792b99..838006d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogStateModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogStateModel.kt
@@ -21,6 +21,8 @@
 /** Models a state of the Volume Dialog. */
 data class VolumeDialogStateModel(
     val shouldShowA11ySlider: Boolean = false,
+    val isShowingSafetyWarning: VolumeDialogSafetyWarningModel =
+        VolumeDialogSafetyWarningModel.Invisible,
     val streamModels: Map<Int, VolumeDialogStreamModel> = mapOf(),
     val ringerModeInternal: Int = 0,
     val ringerModeExternal: Int = 0,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt
index ff525f46..9bab1b0 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt
@@ -18,14 +18,17 @@
 
 import com.android.systemui.volume.Events
 import com.android.systemui.volume.dialog.VolumeDialog
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPlugin
 import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPluginScope
+import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogSafetyWarningInteractor
 import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor
 import com.android.systemui.volume.dialog.shared.VolumeDialogLogger
 import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel
 import javax.inject.Inject
 import javax.inject.Provider
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.mapLatest
 
@@ -34,29 +37,35 @@
 class VolumeDialogPluginViewModel
 @Inject
 constructor(
+    @VolumeDialogPlugin private val coroutineScope: CoroutineScope,
     private val dialogVisibilityInteractor: VolumeDialogVisibilityInteractor,
+    private val dialogSafetyWarningInteractor: VolumeDialogSafetyWarningInteractor,
     private val volumeDialogProvider: Provider<VolumeDialog>,
     private val logger: VolumeDialogLogger,
 ) {
 
-    suspend fun launchVolumeDialog() {
-        coroutineScope {
-            dialogVisibilityInteractor.dialogVisibility
-                .mapLatest { visibilityModel ->
-                    with(visibilityModel) {
-                        if (this is VolumeDialogVisibilityModel.Visible) {
-                            showDialog()
-                            Events.writeEvent(Events.EVENT_SHOW_DIALOG, reason, keyguardLocked)
-                            logger.onShow(reason)
-                        }
-                        if (this is VolumeDialogVisibilityModel.Dismissed) {
-                            Events.writeEvent(Events.EVENT_DISMISS_DIALOG, reason)
-                            logger.onDismiss(reason)
-                        }
+    fun launchVolumeDialog() {
+        dialogVisibilityInteractor.dialogVisibility
+            .mapLatest { visibilityModel ->
+                with(visibilityModel) {
+                    if (this is VolumeDialogVisibilityModel.Visible) {
+                        showDialog()
+                        Events.writeEvent(Events.EVENT_SHOW_DIALOG, reason, keyguardLocked)
+                        logger.onShow(reason)
+                    }
+                    if (this is VolumeDialogVisibilityModel.Dismissed) {
+                        Events.writeEvent(Events.EVENT_DISMISS_DIALOG, reason)
+                        logger.onDismiss(reason)
                     }
                 }
-                .launchIn(this)
-        }
+            }
+            .launchIn(coroutineScope)
+    }
+
+    val isShowingSafetyWarning: Flow<Boolean> = dialogSafetyWarningInteractor.isShowingSafetyWarning
+
+    fun onSafetyWarningDismissed() {
+        dialogSafetyWarningInteractor.onSafetyWarningDismissed()
     }
 
     private fun showDialog() {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt
index 7a6ede4..b20dffb 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt
@@ -44,9 +44,9 @@
 @Inject
 constructor(
     private val context: Context,
-    dialogVisibilityInteractor: VolumeDialogVisibilityInteractor,
+    private val dialogVisibilityInteractor: VolumeDialogVisibilityInteractor,
     volumeDialogSlidersInteractor: VolumeDialogSlidersInteractor,
-    volumeDialogStateInteractor: VolumeDialogStateInteractor,
+    private val volumeDialogStateInteractor: VolumeDialogStateInteractor,
     devicePostureController: DevicePostureController,
     configurationController: ConfigurationController,
 ) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogSafetyWarningInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogSafetyWarningInteractorKosmos.kt
new file mode 100644
index 0000000..ddd0014
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogSafetyWarningInteractorKosmos.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.volumeDialogSafetyWarningInteractor: VolumeDialogSafetyWarningInteractor by
+    Kosmos.Fixture {
+        VolumeDialogSafetyWarningInteractor(
+            volumeDialogStateInteractor,
+            volumeDialogVisibilityInteractor,
+        )
+    }