Merge "[Panlingual] Restore per-app locales for an archived application" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 0ccdf37..ab5d503 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -1043,20 +1043,12 @@
     name: "device_policy_aconfig_flags",
     package: "android.app.admin.flags",
     container: "system",
-    exportable: true,
     srcs: [
         "core/java/android/app/admin/flags/flags.aconfig",
     ],
 }
 
 java_aconfig_library {
-    name: "device_policy_exported_aconfig_flags_lib",
-    aconfig_declarations: "device_policy_aconfig_flags",
-    defaults: ["framework-minus-apex-aconfig-java-defaults"],
-    mode: "exported",
-}
-
-java_aconfig_library {
     name: "device_policy_aconfig_flags_lib",
     aconfig_declarations: "device_policy_aconfig_flags",
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 4ac6bac..604b37d 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -114,6 +114,7 @@
 import com.android.internal.graphics.ColorUtils;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.ContrastColorUtil;
+import com.android.internal.util.NewlineNormalizer;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -3190,7 +3191,7 @@
             return charSequence;
         }
 
-        return charSequence.toString().replaceAll("[\r\n]+", "\n");
+        return NewlineNormalizer.normalizeNewlines(charSequence.toString());
     }
 
     private static CharSequence removeTextSizeSpans(CharSequence charSequence) {
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index efd5a45..ef8501f 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -351,6 +351,12 @@
     }
 
     /** @hide */
+    public boolean isFreeform() {
+        return configuration.windowConfiguration.getWindowingMode()
+                == WindowConfiguration.WINDOWING_MODE_FREEFORM;
+    }
+
+    /** @hide */
     @WindowConfiguration.ActivityType
     public int getActivityType() {
         return configuration.windowConfiguration.getActivityType();
diff --git a/core/java/android/app/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java
index 46c9e78..9ef8b38 100644
--- a/core/java/android/app/admin/DeviceAdminInfo.java
+++ b/core/java/android/app/admin/DeviceAdminInfo.java
@@ -21,7 +21,6 @@
 import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.app.admin.flags.Flags;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
@@ -177,10 +176,6 @@
      * provisioned into "affiliated" mode when on a Headless System User Mode device.
      *
      * <p>This mode adds a Profile Owner to all users other than the user the Device Owner is on.
-     *
-     * <p>Starting from Android version {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM},
-     * DPCs should set the value of attribute "headless-device-owner-mode" inside the
-     * "headless-system-user" tag as "affiliated".
      */
     public static final int HEADLESS_DEVICE_OWNER_MODE_AFFILIATED = 1;
 
@@ -190,10 +185,6 @@
      *
      * <p>This mode only allows a single secondary user on the device blocking the creation of
      * additional secondary users.
-     *
-     * <p>Starting from Android version {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM},
-     * DPCs should set the value of attribute "headless-device-owner-mode" inside the
-     * "headless-system-user" tag as "single_user".
      */
     @FlaggedApi(FLAG_HEADLESS_DEVICE_OWNER_SINGLE_USER_ENABLED)
     public static final int HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER = 2;
@@ -392,30 +383,17 @@
                     }
                     mSupportsTransferOwnership = true;
                 } else if (tagName.equals("headless-system-user")) {
-                    String deviceOwnerModeStringValue = null;
-                    if (Flags.headlessSingleUserCompatibilityFix()) {
-                        deviceOwnerModeStringValue = parser.getAttributeValue(
-                                 null, "headless-device-owner-mode");
-                    }
-                    if (deviceOwnerModeStringValue == null) {
-                        deviceOwnerModeStringValue =
-                                parser.getAttributeValue(null, "device-owner-mode");
-                    }
+                    String deviceOwnerModeStringValue =
+                            parser.getAttributeValue(null, "device-owner-mode");
 
-                    if ("unsupported".equalsIgnoreCase(deviceOwnerModeStringValue)) {
+                    if (deviceOwnerModeStringValue.equalsIgnoreCase("unsupported")) {
                         mHeadlessDeviceOwnerMode = HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED;
-                    } else if ("affiliated".equalsIgnoreCase(deviceOwnerModeStringValue)) {
+                    } else if (deviceOwnerModeStringValue.equalsIgnoreCase("affiliated")) {
                         mHeadlessDeviceOwnerMode = HEADLESS_DEVICE_OWNER_MODE_AFFILIATED;
-                    } else if ("single_user".equalsIgnoreCase(deviceOwnerModeStringValue)) {
+                    } else if (deviceOwnerModeStringValue.equalsIgnoreCase("single_user")) {
                         mHeadlessDeviceOwnerMode = HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER;
                     } else {
-                        if (Flags.headlessSingleUserCompatibilityFix()) {
-                            Log.e(TAG, "Unknown headless-system-user mode: "
-                                    + deviceOwnerModeStringValue);
-                        } else {
-                            throw new XmlPullParserException(
-                                    "headless-system-user mode must be valid");
-                        }
+                        throw new XmlPullParserException("headless-system-user mode must be valid");
                     }
                 }
             }
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index 83daa45..18914e1 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -303,24 +303,3 @@
       purpose: PURPOSE_BUGFIX
     }
 }
-
-flag {
-    name: "headless_single_user_compatibility_fix"
-    namespace: "enterprise"
-    description: "Fix for compatibility issue introduced from using single_user mode on pre-Android V builds"
-    bug: "338050276"
-    is_exported: true
-    metadata {
-      purpose: PURPOSE_BUGFIX
-    }
-}
-
-flag {
-    name: "headless_single_min_target_sdk"
-    namespace: "enterprise"
-    description: "Only allow DPCs targeting Android V to provision into single user mode"
-    bug: "338588825"
-    metadata {
-      purpose: PURPOSE_BUGFIX
-    }
-}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e6ddf35..009713f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5123,13 +5123,6 @@
         public static final String SCREEN_BRIGHTNESS = "screen_brightness";
 
         /**
-         * The screen backlight brightness between 0.0f and 1.0f.
-         * @hide
-         */
-        @Readable
-        public static final String SCREEN_BRIGHTNESS_FLOAT = "screen_brightness_float";
-
-        /**
          * Control whether to enable automatic brightness mode.
          */
         @Readable
@@ -6273,7 +6266,6 @@
             PUBLIC_SETTINGS.add(DIM_SCREEN);
             PUBLIC_SETTINGS.add(SCREEN_OFF_TIMEOUT);
             PUBLIC_SETTINGS.add(SCREEN_BRIGHTNESS);
-            PUBLIC_SETTINGS.add(SCREEN_BRIGHTNESS_FLOAT);
             PUBLIC_SETTINGS.add(SCREEN_BRIGHTNESS_MODE);
             PUBLIC_SETTINGS.add(MODE_RINGER_STREAMS_AFFECTED);
             PUBLIC_SETTINGS.add(MUTE_STREAMS_AFFECTED);
diff --git a/core/java/com/android/internal/util/NewlineNormalizer.java b/core/java/com/android/internal/util/NewlineNormalizer.java
new file mode 100644
index 0000000..0104d1f
--- /dev/null
+++ b/core/java/com/android/internal/util/NewlineNormalizer.java
@@ -0,0 +1,39 @@
+/*
+ * 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.internal.util;
+
+
+import java.util.regex.Pattern;
+
+/**
+ * Utility class that replaces consecutive empty lines with single new line.
+ * @hide
+ */
+public class NewlineNormalizer {
+
+    private static final Pattern MULTIPLE_NEWLINES = Pattern.compile("\\v(\\s*\\v)?");
+
+    // Private constructor to prevent instantiation
+    private NewlineNormalizer() {}
+
+    /**
+     * Replaces consecutive newlines with a single newline in the input text.
+     */
+    public static String normalizeNewlines(String text) {
+        return MULTIPLE_NEWLINES.matcher(text).replaceAll("\n");
+    }
+}
diff --git a/core/tests/utiltests/src/com/android/internal/util/NewlineNormalizerTest.java b/core/tests/utiltests/src/com/android/internal/util/NewlineNormalizerTest.java
new file mode 100644
index 0000000..bcdac61
--- /dev/null
+++ b/core/tests/utiltests/src/com/android/internal/util/NewlineNormalizerTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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.internal.util;
+
+import static junit.framework.Assert.assertEquals;
+
+
+import android.platform.test.annotations.DisabledOnRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test for {@link NewlineNormalizer}
+ * @hide
+ */
+@DisabledOnRavenwood(blockedBy = NewlineNormalizer.class)
+@RunWith(AndroidJUnit4.class)
+public class NewlineNormalizerTest {
+
+    @Rule
+    public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
+    @Test
+    public void testEmptyInput() {
+        assertEquals("", NewlineNormalizer.normalizeNewlines(""));
+    }
+
+    @Test
+    public void testSingleNewline() {
+        assertEquals("\n", NewlineNormalizer.normalizeNewlines("\n"));
+    }
+
+    @Test
+    public void testMultipleConsecutiveNewlines() {
+        assertEquals("\n", NewlineNormalizer.normalizeNewlines("\n\n\n\n\n"));
+    }
+
+    @Test
+    public void testNewlinesWithSpacesAndTabs() {
+        String input = "Line 1\n  \n \t \n\tLine 2";
+        // Adjusted expected output to include the tab character
+        String expected = "Line 1\n\tLine 2";
+        assertEquals(expected, NewlineNormalizer.normalizeNewlines(input));
+    }
+
+    @Test
+    public void testMixedNewlineCharacters() {
+        String input = "Line 1\r\nLine 2\u000BLine 3\fLine 4\u2028Line 5\u2029Line 6";
+        String expected = "Line 1\nLine 2\nLine 3\nLine 4\nLine 5\nLine 6";
+        assertEquals(expected, NewlineNormalizer.normalizeNewlines(input));
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
index f195f95..3ab1fad 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
@@ -81,6 +81,10 @@
         super(context, taskInfo, syncQueue, taskListener, displayLayout);
         mCallback = callback;
         mHasSizeCompat = taskInfo.appCompatTaskInfo.topActivityInSizeCompat;
+        if (Flags.enableDesktopWindowingMode() && Flags.enableWindowingDynamicInitialBounds()) {
+            // Don't show the SCM button for freeform tasks
+            mHasSizeCompat &= !taskInfo.isFreeform();
+        }
         mCameraCompatControlState =
                 taskInfo.appCompatTaskInfo.cameraCompatTaskInfo.cameraCompatControlState;
         mCompatUIHintsState = compatUIHintsState;
@@ -136,6 +140,10 @@
         final boolean prevHasSizeCompat = mHasSizeCompat;
         final int prevCameraCompatControlState = mCameraCompatControlState;
         mHasSizeCompat = taskInfo.appCompatTaskInfo.topActivityInSizeCompat;
+        if (Flags.enableDesktopWindowingMode() && Flags.enableWindowingDynamicInitialBounds()) {
+            // Don't show the SCM button for freeform tasks
+            mHasSizeCompat &= !taskInfo.isFreeform();
+        }
         mCameraCompatControlState =
                 taskInfo.appCompatTaskInfo.cameraCompatTaskInfo.cameraCompatControlState;
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
new file mode 100644
index 0000000..6da3741
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
@@ -0,0 +1,173 @@
+/*
+ * 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.
+ */
+
+@file:JvmName("DesktopModeUtils")
+
+package com.android.wm.shell.desktopmode
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.content.pm.ActivityInfo.isFixedOrientationLandscape
+import android.content.pm.ActivityInfo.isFixedOrientationPortrait
+import android.content.res.Configuration.ORIENTATION_LANDSCAPE
+import android.content.res.Configuration.ORIENTATION_PORTRAIT
+import android.graphics.Rect
+import android.os.SystemProperties
+import android.util.Size
+import com.android.wm.shell.common.DisplayLayout
+
+
+val DESKTOP_MODE_INITIAL_BOUNDS_SCALE: Float = SystemProperties
+        .getInt("persist.wm.debug.desktop_mode_initial_bounds_scale", 75) / 100f
+
+val DESKTOP_MODE_LANDSCAPE_APP_PADDING: Int = SystemProperties
+        .getInt("persist.wm.debug.desktop_mode_landscape_app_padding", 25)
+
+
+/**
+ * Calculates the initial bounds required for an application to fill a scale of the display bounds
+ * without any letterboxing. This is done by taking into account the applications fullscreen size,
+ * aspect ratio, orientation and resizability to calculate an area this is compatible with the
+ * applications previous configuration.
+ */
+fun calculateInitialBounds(
+    displayLayout: DisplayLayout,
+    taskInfo: RunningTaskInfo,
+    scale: Float = DESKTOP_MODE_INITIAL_BOUNDS_SCALE
+): Rect {
+    val screenBounds = Rect(0, 0, displayLayout.width(), displayLayout.height())
+    val appAspectRatio = calculateAspectRatio(taskInfo)
+    val idealSize = calculateIdealSize(screenBounds, scale)
+    // If no top activity exists, apps fullscreen bounds and aspect ratio cannot be calculated.
+    // Instead default to the desired initial bounds.
+    val topActivityInfo = taskInfo.topActivityInfo
+        ?: return positionInScreen(idealSize, screenBounds)
+
+    val initialSize: Size = when (taskInfo.configuration.orientation) {
+        ORIENTATION_LANDSCAPE -> {
+            if (taskInfo.isResizeable) {
+                if (isFixedOrientationPortrait(topActivityInfo.screenOrientation)) {
+                    // Respect apps fullscreen width
+                    Size(taskInfo.appCompatTaskInfo.topActivityLetterboxWidth, idealSize.height)
+                } else {
+                    idealSize
+                }
+            } else {
+                maximumSizeMaintainingAspectRatio(taskInfo, idealSize,
+                    appAspectRatio)
+            }
+        }
+        ORIENTATION_PORTRAIT -> {
+            val customPortraitWidthForLandscapeApp = screenBounds.width() -
+                    (DESKTOP_MODE_LANDSCAPE_APP_PADDING * 2)
+            if (taskInfo.isResizeable) {
+                if (isFixedOrientationLandscape(topActivityInfo.screenOrientation)) {
+                    // Respect apps fullscreen height and apply custom app width
+                    Size(customPortraitWidthForLandscapeApp,
+                        taskInfo.appCompatTaskInfo.topActivityLetterboxHeight)
+                } else {
+                    idealSize
+                }
+            } else {
+                if (isFixedOrientationLandscape(topActivityInfo.screenOrientation)) {
+                    // Apply custom app width and calculate maximum size
+                    maximumSizeMaintainingAspectRatio(
+                        taskInfo,
+                        Size(customPortraitWidthForLandscapeApp, idealSize.height),
+                        appAspectRatio)
+                } else {
+                    maximumSizeMaintainingAspectRatio(taskInfo, idealSize,
+                        appAspectRatio)
+                }
+            }
+        }
+        else -> {
+            idealSize
+        }
+    }
+
+    return positionInScreen(initialSize, screenBounds)
+}
+
+/**
+ * Calculates the largest size that can fit in a given area while maintaining a specific aspect
+ * ratio.
+ */
+private fun maximumSizeMaintainingAspectRatio(
+    taskInfo: RunningTaskInfo,
+    targetArea: Size,
+    aspectRatio: Float
+): Size {
+    val targetHeight = targetArea.height
+    val targetWidth = targetArea.width
+    val finalHeight: Int
+    val finalWidth: Int
+    if (isFixedOrientationPortrait(taskInfo.topActivityInfo!!.screenOrientation)) {
+        val tempWidth = (targetHeight / aspectRatio).toInt()
+        if (tempWidth <= targetWidth) {
+            finalHeight = targetHeight
+            finalWidth = tempWidth
+        } else {
+            finalWidth = targetWidth
+            finalHeight = (finalWidth * aspectRatio).toInt()
+        }
+    } else {
+        val tempWidth = (targetHeight * aspectRatio).toInt()
+        if (tempWidth <= targetWidth) {
+            finalHeight = targetHeight
+            finalWidth = tempWidth
+        } else {
+            finalWidth = targetWidth
+            finalHeight = (finalWidth / aspectRatio).toInt()
+        }
+    }
+    return Size(finalWidth, finalHeight)
+}
+
+/**
+ * Calculates the aspect ratio of an activity from its fullscreen bounds.
+ */
+private fun calculateAspectRatio(taskInfo: RunningTaskInfo): Float {
+    if (taskInfo.appCompatTaskInfo.topActivityBoundsLetterboxed) {
+        val appLetterboxWidth = taskInfo.appCompatTaskInfo.topActivityLetterboxWidth
+        val appLetterboxHeight = taskInfo.appCompatTaskInfo.topActivityLetterboxHeight
+        return maxOf(appLetterboxWidth, appLetterboxHeight) /
+                minOf(appLetterboxWidth, appLetterboxHeight).toFloat()
+    }
+    val appBounds = taskInfo.configuration.windowConfiguration.appBounds ?: return 1f
+    return maxOf(appBounds.height(), appBounds.width()) /
+                minOf(appBounds.height(), appBounds.width()).toFloat()
+}
+
+/**
+ * Calculates the desired initial bounds for applications in desktop windowing. This is done as a
+ * scale of the screen bounds.
+ */
+private fun calculateIdealSize(screenBounds: Rect, scale: Float): Size {
+    val width = (screenBounds.width() * scale).toInt()
+    val height = (screenBounds.height() * scale).toInt()
+    return Size(width, height)
+}
+
+/**
+ * Adjusts bounds to be positioned in the middle of the screen.
+ */
+private fun positionInScreen(desiredSize: Size, screenBounds: Rect): Rect {
+    // TODO(b/325240051): Position apps with bottom heavy offset
+    val heightOffset = (screenBounds.height() - desiredSize.height) / 2
+    val widthOffset = (screenBounds.width() - desiredSize.width) / 2
+    return Rect(widthOffset, heightOffset,
+        desiredSize.width + widthOffset, desiredSize.height + heightOffset)
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index f7bfb86..b0d5923 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -47,6 +47,7 @@
 import android.window.TransitionRequestInfo
 import android.window.WindowContainerTransaction
 import androidx.annotation.BinderThread
+import com.android.internal.annotations.VisibleForTesting
 import com.android.internal.policy.ScreenDecorationsUtils
 import com.android.window.flags.Flags
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
@@ -85,7 +86,6 @@
 import com.android.wm.shell.windowdecor.DragPositioningCallbackUtility
 import com.android.wm.shell.windowdecor.MoveToDesktopAnimator
 import com.android.wm.shell.windowdecor.OnTaskResizeAnimationListener
-import com.android.wm.shell.windowdecor.extension.isFreeform
 import com.android.wm.shell.windowdecor.extension.isFullscreen
 import java.io.PrintWriter
 import java.util.Optional
@@ -203,6 +203,11 @@
         dragAndDropController.addListener(this)
     }
 
+    @VisibleForTesting
+    fun getVisualIndicator(): DesktopModeVisualIndicator? {
+        return visualIndicator
+    }
+
     fun setOnTaskResizeAnimationListener(listener: OnTaskResizeAnimationListener) {
         toggleResizeDesktopTaskTransitionHandler.setOnTaskResizeAnimationListener(listener)
         enterDesktopTaskTransitionHandler.setOnTaskResizeAnimationListener(listener)
@@ -605,8 +610,9 @@
     }
 
     /**
-     * Quick-resizes a desktop task, toggling between the stable bounds and the last saved bounds
-     * if available or the default bounds otherwise.
+     * Quick-resizes a desktop task, toggling between a fullscreen state (represented by the
+     * stable bounds) and a free floating state (either the last saved bounds if available or the
+     * default bounds otherwise).
      */
     fun toggleDesktopTaskSize(taskInfo: RunningTaskInfo) {
         val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return
@@ -623,7 +629,11 @@
             if (taskBoundsBeforeMaximize != null) {
                 destinationBounds.set(taskBoundsBeforeMaximize)
             } else {
-                destinationBounds.set(getDefaultDesktopTaskBounds(displayLayout))
+                if (Flags.enableWindowingDynamicInitialBounds()){
+                    destinationBounds.set(calculateInitialBounds(displayLayout, taskInfo))
+                } else {
+                    destinationBounds.set(getDefaultDesktopTaskBounds(displayLayout))
+                }
             }
         } else {
             // Save current bounds so that task can be restored back to original bounds if necessary
@@ -1011,6 +1021,7 @@
         wct: WindowContainerTransaction,
         taskInfo: RunningTaskInfo
     ) {
+        val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return
         val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(taskInfo.displayId)!!
         val tdaWindowingMode = tdaInfo.configuration.windowConfiguration.windowingMode
         val targetWindowingMode = if (tdaWindowingMode == WINDOWING_MODE_FREEFORM) {
@@ -1019,6 +1030,9 @@
         } else {
             WINDOWING_MODE_FREEFORM
         }
+        if (Flags.enableWindowingDynamicInitialBounds()) {
+            wct.setBounds(taskInfo.token, calculateInitialBounds(displayLayout, taskInfo))
+        }
         wct.setWindowingMode(taskInfo.token, targetWindowingMode)
         wct.reorder(taskInfo.token, true /* onTop */)
         if (isDesktopDensityOverrideSet()) {
@@ -1239,13 +1253,17 @@
      * @param y height of drag, to be checked against status bar height.
      */
     fun onDragPositioningEndThroughStatusBar(inputCoordinates: PointF, taskInfo: RunningTaskInfo) {
-        val indicator = visualIndicator ?: return
+        val indicator = getVisualIndicator() ?: return
         val indicatorType = indicator
             .updateIndicatorType(inputCoordinates, taskInfo.windowingMode)
         when (indicatorType) {
             DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR -> {
                 val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return
-                finalizeDragToDesktop(taskInfo, getDefaultDesktopTaskBounds(displayLayout))
+                if (Flags.enableWindowingDynamicInitialBounds()) {
+                    finalizeDragToDesktop(taskInfo, calculateInitialBounds(displayLayout, taskInfo))
+                } else {
+                    finalizeDragToDesktop(taskInfo, getDefaultDesktopTaskBounds(displayLayout))
+                }
             }
             DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR,
             DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR -> {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index f7516da..4c347ad 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -110,6 +110,8 @@
 
     private ResizeVeil mResizeVeil;
     private Bitmap mAppIconBitmap;
+    private Bitmap mResizeVeilBitmap;
+
     private CharSequence mAppName;
 
     private ExclusionRegionListener mExclusionRegionListener;
@@ -468,11 +470,15 @@
             PackageManager pm = mContext.getApplicationContext().getPackageManager();
             final IconProvider provider = new IconProvider(mContext);
             final Drawable appIconDrawable = provider.getIcon(activityInfo);
-            final Resources resources = mContext.getResources();
-            final BaseIconFactory factory = new BaseIconFactory(mContext,
-                    resources.getDisplayMetrics().densityDpi,
-                    resources.getDimensionPixelSize(R.dimen.desktop_mode_caption_icon_radius));
-            mAppIconBitmap = factory.createScaledBitmap(appIconDrawable, MODE_DEFAULT);
+            final BaseIconFactory headerIconFactory = createIconFactory(mContext,
+                    R.dimen.desktop_mode_caption_icon_radius);
+            mAppIconBitmap = headerIconFactory.createScaledBitmap(appIconDrawable, MODE_DEFAULT);
+
+            final BaseIconFactory resizeVeilIconFactory = createIconFactory(mContext,
+                    R.dimen.desktop_mode_resize_veil_icon_size);
+            mResizeVeilBitmap = resizeVeilIconFactory
+                    .createScaledBitmap(appIconDrawable, MODE_DEFAULT);
+
             final ApplicationInfo applicationInfo = activityInfo.applicationInfo;
             mAppName = pm.getApplicationLabel(applicationInfo);
         } finally {
@@ -480,6 +486,13 @@
         }
     }
 
+    private BaseIconFactory createIconFactory(Context context, int dimensions) {
+        final Resources resources = context.getResources();
+        final int densityDpi = resources.getDisplayMetrics().densityDpi;
+        final int iconSize = resources.getDimensionPixelSize(dimensions);
+        return new BaseIconFactory(context, densityDpi, iconSize);
+    }
+
     private void closeDragResizeListener() {
         if (mDragResizeListener == null) {
             return;
@@ -495,7 +508,7 @@
     private void createResizeVeilIfNeeded() {
         if (mResizeVeil != null) return;
         loadAppInfoIfNeeded();
-        mResizeVeil = new ResizeVeil(mContext, mDisplayController, mAppIconBitmap, mTaskInfo,
+        mResizeVeil = new ResizeVeil(mContext, mDisplayController, mResizeVeilBitmap, mTaskInfo,
                 mTaskSurface, mSurfaceControlTransactionSupplier);
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/extension/TaskInfo.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/extension/TaskInfo.kt
index a2293d5..ec20471 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/extension/TaskInfo.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/extension/TaskInfo.kt
@@ -17,7 +17,6 @@
 package com.android.wm.shell.windowdecor.extension
 
 import android.app.TaskInfo
-import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
 import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
 import android.view.WindowInsetsController.APPEARANCE_LIGHT_CAPTION_BARS
 import android.view.WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND
@@ -36,6 +35,3 @@
 
 val TaskInfo.isFullscreen: Boolean
     get() = windowingMode == WINDOWING_MODE_FULLSCREEN
-
-val TaskInfo.isFreeform: Boolean
-    get() = windowingMode == WINDOWING_MODE_FREEFORM
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index df8a222..3f76c4f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -24,6 +24,12 @@
 import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
 import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
 import android.content.Intent
+import android.content.pm.ActivityInfo
+import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
+import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
+import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
+import android.content.res.Configuration.ORIENTATION_LANDSCAPE
+import android.content.res.Configuration.ORIENTATION_PORTRAIT
 import android.graphics.Point
 import android.graphics.PointF
 import android.graphics.Rect
@@ -101,7 +107,9 @@
 import org.mockito.Mockito.anyInt
 import org.mockito.Mockito.clearInvocations
 import org.mockito.Mockito.mock
+import org.mockito.Mockito.spy
 import org.mockito.Mockito.verify
+import org.mockito.kotlin.anyOrNull
 import org.mockito.kotlin.atLeastOnce
 import org.mockito.kotlin.capture
 import org.mockito.quality.Strictness
@@ -141,6 +149,7 @@
     @Mock lateinit var dragAndDropController: DragAndDropController
     @Mock lateinit var multiInstanceHelper: MultiInstanceHelper
     @Mock lateinit var desktopModeLoggerTransitionObserver: DesktopModeLoggerTransitionObserver
+    @Mock lateinit var desktopModeVisualIndicator: DesktopModeVisualIndicator
 
     private lateinit var mockitoSession: StaticMockitoSession
     private lateinit var controller: DesktopTasksController
@@ -154,6 +163,15 @@
     // Mock running tasks are registered here so we can get the list from mock shell task organizer
     private val runningTasks = mutableListOf<RunningTaskInfo>()
 
+    private val DISPLAY_DIMENSION_SHORT = 1600
+    private val DISPLAY_DIMENSION_LONG = 2560
+    private val DEFAULT_LANDSCAPE_BOUNDS = Rect(320, 200, 2240, 1400)
+    private val DEFAULT_PORTRAIT_BOUNDS = Rect(200, 320, 1400, 2240)
+    private val RESIZABLE_LANDSCAPE_BOUNDS = Rect(25, 680, 1575, 1880)
+    private val RESIZABLE_PORTRAIT_BOUNDS = Rect(680, 200, 1880, 1400)
+    private val UNRESIZABLE_LANDSCAPE_BOUNDS = Rect(25, 699, 1575, 1861)
+    private val UNRESIZABLE_PORTRAIT_BOUNDS = Rect(830, 200, 1730, 1400)
+
     @Before
     fun setUp() {
         mockitoSession = mockitoSession().strictness(Strictness.LENIENT)
@@ -161,7 +179,7 @@
         whenever(DesktopModeStatus.isEnabled()).thenReturn(true)
         doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
 
-        shellInit = Mockito.spy(ShellInit(testExecutor))
+        shellInit = spy(ShellInit(testExecutor))
         desktopModeTaskRepository = DesktopModeTaskRepository()
         desktopTasksLimiter =
                 DesktopTasksLimiter(transitions, desktopModeTaskRepository, shellTaskOrganizer)
@@ -464,6 +482,135 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    fun moveToDesktop_landscapeDevice_resizable_undefinedOrientation_defaultLandscapeBounds() {
+        doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+        val task = setUpFullscreenTask()
+        setUpLandscapeDisplay()
+
+        controller.moveToDesktop(task)
+        val wct = getLatestMoveToDesktopWct()
+        assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    fun moveToDesktop_landscapeDevice_resizable_landscapeOrientation_defaultLandscapeBounds() {
+        doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+        val task = setUpFullscreenTask(screenOrientation = SCREEN_ORIENTATION_LANDSCAPE)
+        setUpLandscapeDisplay()
+
+        controller.moveToDesktop(task)
+        val wct = getLatestMoveToDesktopWct()
+        assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    fun moveToDesktop_landscapeDevice_resizable_portraitOrientation_resizablePortraitBounds() {
+        doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+        val task = setUpFullscreenTask(screenOrientation = SCREEN_ORIENTATION_PORTRAIT,
+            shouldLetterbox = true)
+        setUpLandscapeDisplay()
+
+        controller.moveToDesktop(task)
+        val wct = getLatestMoveToDesktopWct()
+        assertThat(findBoundsChange(wct, task)).isEqualTo(RESIZABLE_PORTRAIT_BOUNDS)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    fun moveToDesktop_landscapeDevice_unResizable_landscapeOrientation_defaultLandscapeBounds() {
+        doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+        val task = setUpFullscreenTask(isResizable = false,
+            screenOrientation = SCREEN_ORIENTATION_LANDSCAPE)
+        setUpLandscapeDisplay()
+
+        controller.moveToDesktop(task)
+        val wct = getLatestMoveToDesktopWct()
+        assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    fun moveToDesktop_landscapeDevice_unResizable_portraitOrientation_unResizablePortraitBounds() {
+        doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+        val task = setUpFullscreenTask(isResizable = false,
+            screenOrientation = SCREEN_ORIENTATION_PORTRAIT, shouldLetterbox = true)
+        setUpLandscapeDisplay()
+
+        controller.moveToDesktop(task)
+        val wct = getLatestMoveToDesktopWct()
+        assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_PORTRAIT_BOUNDS)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    fun moveToDesktop_portraitDevice_resizable_undefinedOrientation_defaultPortraitBounds() {
+        doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+        val task = setUpFullscreenTask(deviceOrientation = ORIENTATION_PORTRAIT)
+        setUpPortraitDisplay()
+
+        controller.moveToDesktop(task)
+        val wct = getLatestMoveToDesktopWct()
+        assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    fun moveToDesktop_portraitDevice_resizable_portraitOrientation_defaultPortraitBounds() {
+        doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+        val task = setUpFullscreenTask(deviceOrientation = ORIENTATION_PORTRAIT,
+            screenOrientation = SCREEN_ORIENTATION_PORTRAIT)
+        setUpPortraitDisplay()
+
+        controller.moveToDesktop(task)
+        val wct = getLatestMoveToDesktopWct()
+        assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    fun moveToDesktop_portraitDevice_resizable_landscapeOrientation_resizableLandscapeBounds() {
+        doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+        val task = setUpFullscreenTask(deviceOrientation = ORIENTATION_PORTRAIT,
+            screenOrientation = SCREEN_ORIENTATION_LANDSCAPE, shouldLetterbox = true)
+        setUpPortraitDisplay()
+
+        controller.moveToDesktop(task)
+        val wct = getLatestMoveToDesktopWct()
+        assertThat(findBoundsChange(wct, task)).isEqualTo(RESIZABLE_LANDSCAPE_BOUNDS)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    fun moveToDesktop_portraitDevice_unResizable_portraitOrientation_defaultPortraitBounds() {
+        doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+        val task = setUpFullscreenTask(isResizable = false,
+            deviceOrientation = ORIENTATION_PORTRAIT,
+            screenOrientation = SCREEN_ORIENTATION_PORTRAIT)
+        setUpPortraitDisplay()
+
+        controller.moveToDesktop(task)
+        val wct = getLatestMoveToDesktopWct()
+        assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    fun moveToDesktop_portraitDevice_unResizable_landscapeOrientation_unResizableLandscapeBounds() {
+        doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+        val task = setUpFullscreenTask(isResizable = false,
+            deviceOrientation = ORIENTATION_PORTRAIT,
+            screenOrientation = SCREEN_ORIENTATION_LANDSCAPE, shouldLetterbox = true)
+        setUpPortraitDisplay()
+
+        controller.moveToDesktop(task)
+        val wct = getLatestMoveToDesktopWct()
+        assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_LANDSCAPE_BOUNDS)
+    }
+
+    @Test
     fun moveToDesktop_tdaFullscreen_windowingModeSetToFreeform() {
         val task = setUpFullscreenTask()
         val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
@@ -1225,6 +1372,185 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    fun dragToDesktop_landscapeDevice_resizable_undefinedOrientation_defaultLandscapeBounds() {
+        doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+        val spyController = spy(controller)
+        whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+        whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
+                .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+        val task = setUpFullscreenTask()
+        setUpLandscapeDisplay()
+
+        spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+        val wct = getLatestDragToDesktopWct()
+        assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    fun dragToDesktop_landscapeDevice_resizable_landscapeOrientation_defaultLandscapeBounds() {
+        doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+        val spyController = spy(controller)
+        whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+        whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
+                .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+        val task = setUpFullscreenTask(screenOrientation = SCREEN_ORIENTATION_LANDSCAPE)
+        setUpLandscapeDisplay()
+
+        spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+        val wct = getLatestDragToDesktopWct()
+        assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    fun dragToDesktop_landscapeDevice_resizable_portraitOrientation_resizablePortraitBounds() {
+        doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+        val spyController = spy(controller)
+        whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+        whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
+                .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+        val task = setUpFullscreenTask(screenOrientation = SCREEN_ORIENTATION_PORTRAIT,
+            shouldLetterbox = true)
+        setUpLandscapeDisplay()
+
+        spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+        val wct = getLatestDragToDesktopWct()
+        assertThat(findBoundsChange(wct, task)).isEqualTo(RESIZABLE_PORTRAIT_BOUNDS)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    fun dragToDesktop_landscapeDevice_unResizable_landscapeOrientation_defaultLandscapeBounds() {
+        doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+        val spyController = spy(controller)
+        whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+        whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
+                .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+        val task = setUpFullscreenTask(isResizable = false,
+            screenOrientation = SCREEN_ORIENTATION_LANDSCAPE)
+        setUpLandscapeDisplay()
+
+        spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+        val wct = getLatestDragToDesktopWct()
+        assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    fun dragToDesktop_landscapeDevice_unResizable_portraitOrientation_unResizablePortraitBounds() {
+        doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+        val spyController = spy(controller)
+        whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+        whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
+                .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+        val task = setUpFullscreenTask(isResizable = false,
+            screenOrientation = SCREEN_ORIENTATION_PORTRAIT, shouldLetterbox = true)
+        setUpLandscapeDisplay()
+
+        spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+        val wct = getLatestDragToDesktopWct()
+        assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_PORTRAIT_BOUNDS)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    fun dragToDesktop_portraitDevice_resizable_undefinedOrientation_defaultPortraitBounds() {
+        doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+        val spyController = spy(controller)
+        whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+        whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
+                .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+        val task = setUpFullscreenTask(deviceOrientation = ORIENTATION_PORTRAIT)
+        setUpPortraitDisplay()
+
+        spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+        val wct = getLatestDragToDesktopWct()
+        assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    fun dragToDesktop_portraitDevice_resizable_portraitOrientation_defaultPortraitBounds() {
+        doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+        val spyController = spy(controller)
+        whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+        whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
+                .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+        val task = setUpFullscreenTask(deviceOrientation = ORIENTATION_PORTRAIT,
+            screenOrientation = SCREEN_ORIENTATION_PORTRAIT)
+        setUpPortraitDisplay()
+
+        spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+        val wct = getLatestDragToDesktopWct()
+        assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    fun dragToDesktop_portraitDevice_resizable_landscapeOrientation_resizableLandscapeBounds() {
+        doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+        val spyController = spy(controller)
+        whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+        whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
+                .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+        val task = setUpFullscreenTask(deviceOrientation = ORIENTATION_PORTRAIT,
+            screenOrientation = SCREEN_ORIENTATION_LANDSCAPE, shouldLetterbox = true)
+        setUpPortraitDisplay()
+
+        spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+        val wct = getLatestDragToDesktopWct()
+        assertThat(findBoundsChange(wct, task)).isEqualTo(RESIZABLE_LANDSCAPE_BOUNDS)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    fun dragToDesktop_portraitDevice_unResizable_portraitOrientation_defaultPortraitBounds() {
+        doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+        val spyController = spy(controller)
+        whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+        whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
+                .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+        val task = setUpFullscreenTask(isResizable = false,
+            deviceOrientation = ORIENTATION_PORTRAIT,
+            screenOrientation = SCREEN_ORIENTATION_PORTRAIT)
+        setUpPortraitDisplay()
+
+        spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+        val wct = getLatestDragToDesktopWct()
+        assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    fun dragToDesktop_portraitDevice_unResizable_landscapeOrientation_unResizableLandscapeBounds() {
+        doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+        val spyController = spy(controller)
+        whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+        whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
+                .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+        val task = setUpFullscreenTask(isResizable = false,
+            deviceOrientation = ORIENTATION_PORTRAIT,
+            screenOrientation = SCREEN_ORIENTATION_LANDSCAPE, shouldLetterbox = true)
+        setUpPortraitDisplay()
+
+        spyController.onDragPositioningEndThroughStatusBar(PointF(200f, 200f), task)
+        val wct = getLatestDragToDesktopWct()
+        assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_LANDSCAPE_BOUNDS)
+    }
+
+    @Test
     fun onDesktopDragMove_endsOutsideValidDragArea_snapsToValidBounds() {
         val task = setUpFreeformTask()
         val mockSurface = mock(SurfaceControl::class.java)
@@ -1276,8 +1602,7 @@
         controller.toggleDesktopTaskSize(task)
         // Assert bounds set to stable bounds
         val wct = getLatestToggleResizeDesktopTaskWct()
-        assertThat(wct.changes[task.token.asBinder()]?.configuration?.windowConfiguration?.bounds)
-                .isEqualTo(STABLE_BOUNDS)
+        assertThat(findBoundsChange(wct, task)).isEqualTo(STABLE_BOUNDS)
     }
 
     @Test
@@ -1304,8 +1629,7 @@
 
         // Assert bounds set to last bounds before maximize
         val wct = getLatestToggleResizeDesktopTaskWct()
-        assertThat(wct.changes[task.token.asBinder()]?.configuration?.windowConfiguration?.bounds)
-                .isEqualTo(boundsBeforeMaximize)
+        assertThat(findBoundsChange(wct, task)).isEqualTo(boundsBeforeMaximize)
     }
 
     @Test
@@ -1346,14 +1670,65 @@
         return task
     }
 
-    private fun setUpFullscreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
+    private fun setUpFullscreenTask(
+        displayId: Int = DEFAULT_DISPLAY,
+        isResizable: Boolean = true,
+        windowingMode: Int = WINDOWING_MODE_FULLSCREEN,
+        deviceOrientation: Int = ORIENTATION_LANDSCAPE,
+        screenOrientation: Int = SCREEN_ORIENTATION_UNSPECIFIED,
+        shouldLetterbox: Boolean = false
+    ): RunningTaskInfo {
         val task = createFullscreenTask(displayId)
+        val activityInfo = ActivityInfo()
+        activityInfo.screenOrientation = screenOrientation
+        with(task) {
+            topActivityInfo = activityInfo
+            isResizeable = isResizable
+            configuration.orientation = deviceOrientation
+            configuration.windowConfiguration.windowingMode = windowingMode
+
+            if (shouldLetterbox) {
+                if (deviceOrientation == ORIENTATION_LANDSCAPE &&
+                    screenOrientation == SCREEN_ORIENTATION_PORTRAIT) {
+                    // Letterbox to portrait size
+                    appCompatTaskInfo.topActivityBoundsLetterboxed = true
+                    appCompatTaskInfo.topActivityLetterboxWidth = 1200
+                    appCompatTaskInfo.topActivityLetterboxHeight = 1600
+                } else if (deviceOrientation == ORIENTATION_PORTRAIT &&
+                    screenOrientation == SCREEN_ORIENTATION_LANDSCAPE) {
+                    // Letterbox to landscape size
+                    appCompatTaskInfo.topActivityBoundsLetterboxed = true
+                    appCompatTaskInfo.topActivityLetterboxWidth = 1600
+                    appCompatTaskInfo.topActivityLetterboxHeight = 1200
+                }
+            } else {
+                appCompatTaskInfo.topActivityBoundsLetterboxed = false
+            }
+
+            if (deviceOrientation == ORIENTATION_LANDSCAPE) {
+                configuration.windowConfiguration.appBounds = Rect(0, 0,
+                    DISPLAY_DIMENSION_LONG, DISPLAY_DIMENSION_SHORT)
+            } else {
+                configuration.windowConfiguration.appBounds = Rect(0, 0,
+                    DISPLAY_DIMENSION_SHORT, DISPLAY_DIMENSION_LONG)
+            }
+        }
         whenever(DesktopModeStatus.enforceDeviceRestrictions()).thenReturn(true)
         whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
         runningTasks.add(task)
         return task
     }
 
+    private fun setUpLandscapeDisplay() {
+        whenever(displayLayout.width()).thenReturn(DISPLAY_DIMENSION_LONG)
+        whenever(displayLayout.height()).thenReturn(DISPLAY_DIMENSION_SHORT)
+    }
+
+    private fun setUpPortraitDisplay() {
+        whenever(displayLayout.width()).thenReturn(DISPLAY_DIMENSION_SHORT)
+        whenever(displayLayout.height()).thenReturn(DISPLAY_DIMENSION_LONG)
+    }
+
     private fun setUpSplitScreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
         val task = createSplitScreenTask(displayId)
         whenever(DesktopModeStatus.enforceDeviceRestrictions()).thenReturn(true)
@@ -1418,6 +1793,17 @@
         return arg.value
     }
 
+    private fun getLatestDragToDesktopWct(): WindowContainerTransaction {
+        val arg: ArgumentCaptor<WindowContainerTransaction> =
+            ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+        if (ENABLE_SHELL_TRANSITIONS) {
+            verify(dragToDesktopTransitionHandler).finishDragToDesktopTransition(capture(arg))
+        } else {
+            verify(shellTaskOrganizer).applyTransaction(capture(arg))
+        }
+        return arg.value
+    }
+
     private fun getLatestExitDesktopWct(): WindowContainerTransaction {
         val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
         if (ENABLE_SHELL_TRANSITIONS) {
@@ -1429,6 +1815,10 @@
         return arg.value
     }
 
+    private fun findBoundsChange(wct: WindowContainerTransaction, task: RunningTaskInfo): Rect? =
+        wct.changes[task.token.asBinder()]?.configuration?.windowConfiguration?.bounds
+
+
     private fun verifyWCTNotExecuted() {
         if (ENABLE_SHELL_TRANSITIONS) {
             verify(transitions, never()).startTransition(anyInt(), any(), isNull())
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 461b6b3..70ce202 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -2949,9 +2949,6 @@
         dumpSetting(s, p,
                 Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ,
                 SystemSettingsProto.Screen.AUTO_BRIGHTNESS_ADJ);
-        dumpSetting(s, p,
-                Settings.System.SCREEN_BRIGHTNESS_FLOAT,
-                SystemSettingsProto.Screen.BRIGHTNESS_FLOAT);
         p.end(screenToken);
 
         dumpSetting(s, p,
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index c891dfc..92167ee 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -935,7 +935,6 @@
                         Settings.System.VOLUME_VOICE, // deprecated since API 2?
                         Settings.System.WHEN_TO_MAKE_WIFI_CALLS, // bug?
                         Settings.System.WINDOW_ORIENTATION_LISTENER_LOG, // used for debugging only
-                        Settings.System.SCREEN_BRIGHTNESS_FLOAT,
                         Settings.System.SCREEN_BRIGHTNESS_FOR_ALS,
                         Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE,
                         Settings.System.WEAR_TTS_PREWARM_ENABLED,
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 21881f6..80398cd 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -550,6 +550,13 @@
 }
 
 flag {
+    name: "enable_contextual_tip_for_mute_volume"
+    namespace: "systemui"
+    description: "Enables the contextual tip for muting the volume."
+    bug: "337737048"
+}
+
+flag {
    name: "disable_contextual_tips_frequency_check"
    description: "Disables frequency capping check for contextual tips."
    namespace: "systemui"
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
index 1e60b98..d4660fa 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
@@ -44,6 +44,7 @@
 import com.android.internal.annotations.VisibleForTesting
 import com.android.internal.policy.ScreenDecorationsUtils
 import com.android.systemui.Flags.activityTransitionUseLargestWindow
+import java.util.concurrent.Executor
 import kotlin.math.roundToInt
 
 private const val TAG = "ActivityTransitionAnimator"
@@ -52,14 +53,19 @@
  * A class that allows activities to be started in a seamless way from a view that is transforming
  * nicely into the starting window.
  */
-class ActivityTransitionAnimator(
+class ActivityTransitionAnimator
+@JvmOverloads
+constructor(
+    /** The executor that runs on the main thread. */
+    private val mainExecutor: Executor,
+
     /** The animator used when animating a View into an app. */
-    private val transitionAnimator: TransitionAnimator = DEFAULT_TRANSITION_ANIMATOR,
+    private val transitionAnimator: TransitionAnimator = defaultTransitionAnimator(mainExecutor),
 
     /** The animator used when animating a Dialog into an app. */
     // TODO(b/218989950): Remove this animator and instead set the duration of the dim fade out to
     // TIMINGS.contentBeforeFadeOutDuration.
-    private val dialogToAppAnimator: TransitionAnimator = DEFAULT_DIALOG_TO_APP_ANIMATOR,
+    private val dialogToAppAnimator: TransitionAnimator = defaultDialogToAppAnimator(mainExecutor),
 
     /**
      * Whether we should disable the WindowManager timeout. This should be set to true in tests
@@ -100,10 +106,6 @@
         // TODO(b/288507023): Remove this flag.
         @JvmField val DEBUG_TRANSITION_ANIMATION = Build.IS_DEBUGGABLE
 
-        private val DEFAULT_TRANSITION_ANIMATOR = TransitionAnimator(TIMINGS, INTERPOLATORS)
-        private val DEFAULT_DIALOG_TO_APP_ANIMATOR =
-            TransitionAnimator(DIALOG_TIMINGS, INTERPOLATORS)
-
         /** Durations & interpolators for the navigation bar fading in & out. */
         private const val ANIMATION_DURATION_NAV_FADE_IN = 266L
         private const val ANIMATION_DURATION_NAV_FADE_OUT = 133L
@@ -121,6 +123,14 @@
          * cancelled by WM.
          */
         private const val LONG_TRANSITION_TIMEOUT = 5_000L
+
+        private fun defaultTransitionAnimator(mainExecutor: Executor): TransitionAnimator {
+            return TransitionAnimator(mainExecutor, TIMINGS, INTERPOLATORS)
+        }
+
+        private fun defaultDialogToAppAnimator(mainExecutor: Executor): TransitionAnimator {
+            return TransitionAnimator(mainExecutor, DIALOG_TIMINGS, INTERPOLATORS)
+        }
     }
 
     /**
@@ -257,9 +267,7 @@
 
     private fun Controller.callOnIntentStartedOnMainThread(willAnimate: Boolean) {
         if (Looper.myLooper() != Looper.getMainLooper()) {
-            this.transitionContainer.context.mainExecutor.execute {
-                callOnIntentStartedOnMainThread(willAnimate)
-            }
+            mainExecutor.execute { callOnIntentStartedOnMainThread(willAnimate) }
         } else {
             if (DEBUG_TRANSITION_ANIMATION) {
                 Log.d(
@@ -479,12 +487,10 @@
         controller: Controller,
         callback: Callback,
         /** The animator to use to animate the window transition. */
-        transitionAnimator: TransitionAnimator = DEFAULT_TRANSITION_ANIMATOR,
+        transitionAnimator: TransitionAnimator,
         /** Listener for animation lifecycle events. */
         listener: Listener? = null
     ) : IRemoteAnimationRunner.Stub() {
-        private val context = controller.transitionContainer.context
-
         // This is being passed across IPC boundaries and cycles (through PendingIntentRecords,
         // etc.) are possible. So we need to make sure we drop any references that might
         // transitively cause leaks when we're done with animation.
@@ -493,11 +499,12 @@
         init {
             delegate =
                 AnimationDelegate(
+                    mainExecutor,
                     controller,
                     callback,
                     DelegatingAnimationCompletionListener(listener, this::dispose),
                     transitionAnimator,
-                    disableWmTimeout
+                    disableWmTimeout,
                 )
         }
 
@@ -510,7 +517,7 @@
             finishedCallback: IRemoteAnimationFinishedCallback?
         ) {
             val delegate = delegate
-            context.mainExecutor.execute {
+            mainExecutor.execute {
                 if (delegate == null) {
                     Log.i(TAG, "onAnimationStart called after completion")
                     // Animation started too late and timed out already. We need to still
@@ -525,7 +532,7 @@
         @BinderThread
         override fun onAnimationCancelled() {
             val delegate = delegate
-            context.mainExecutor.execute {
+            mainExecutor.execute {
                 delegate ?: Log.wtf(TAG, "onAnimationCancelled called after completion")
                 delegate?.onAnimationCancelled()
             }
@@ -535,19 +542,21 @@
         fun dispose() {
             // Drop references to animation controller once we're done with the animation
             // to avoid leaking.
-            context.mainExecutor.execute { delegate = null }
+            mainExecutor.execute { delegate = null }
         }
     }
 
     class AnimationDelegate
     @JvmOverloads
     constructor(
+        private val mainExecutor: Executor,
         private val controller: Controller,
         private val callback: Callback,
         /** Listener for animation lifecycle events. */
         private val listener: Listener? = null,
         /** The animator to use to animate the window transition. */
-        private val transitionAnimator: TransitionAnimator = DEFAULT_TRANSITION_ANIMATOR,
+        private val transitionAnimator: TransitionAnimator =
+            defaultTransitionAnimator(mainExecutor),
 
         /**
          * Whether we should disable the WindowManager timeout. This should be set to true in tests
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogTransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogTransitionAnimator.kt
index b89ebfc..f5d01d7 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogTransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogTransitionAnimator.kt
@@ -37,6 +37,7 @@
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.systemui.util.maybeForceFullscreen
 import com.android.systemui.util.registerAnimationOnBackInvoked
+import java.util.concurrent.Executor
 import kotlin.math.roundToInt
 
 private const val TAG = "DialogTransitionAnimator"
@@ -55,10 +56,16 @@
 class DialogTransitionAnimator
 @JvmOverloads
 constructor(
+    private val mainExecutor: Executor,
     private val callback: Callback,
     private val interactionJankMonitor: InteractionJankMonitor,
     private val featureFlags: AnimationFeatureFlags,
-    private val transitionAnimator: TransitionAnimator = TransitionAnimator(TIMINGS, INTERPOLATORS),
+    private val transitionAnimator: TransitionAnimator =
+        TransitionAnimator(
+            mainExecutor,
+            TIMINGS,
+            INTERPOLATORS,
+        ),
     private val isForTesting: Boolean = false,
 ) {
     private companion object {
@@ -937,24 +944,9 @@
                 }
 
                 override fun onTransitionAnimationEnd(isExpandingFullyAbove: Boolean) {
-                    // onLaunchAnimationEnd is called by an Animator at the end of the animation,
-                    // on a Choreographer animation tick. The following calls will move the animated
-                    // content from the dialog overlay back to its original position, and this
-                    // change must be reflected in the next frame given that we then sync the next
-                    // frame of both the content and dialog ViewRoots. However, in case that content
-                    // is rendered by Compose, whose compositions are also scheduled on a
-                    // Choreographer frame, any state change made *right now* won't be reflected in
-                    // the next frame given that a Choreographer frame can't schedule another and
-                    // have it happen in the same frame. So we post the forwarded calls to
-                    // [Controller.onLaunchAnimationEnd], leaving this Choreographer frame, ensuring
-                    // that the move of the content back to its original window will be reflected in
-                    // the next frame right after [onLaunchAnimationEnd] is called.
-                    dialog.context.mainExecutor.execute {
-                        startController.onTransitionAnimationEnd(isExpandingFullyAbove)
-                        endController.onTransitionAnimationEnd(isExpandingFullyAbove)
-
-                        onLaunchAnimationEnd()
-                    }
+                    startController.onTransitionAnimationEnd(isExpandingFullyAbove)
+                    endController.onTransitionAnimationEnd(isExpandingFullyAbove)
+                    onLaunchAnimationEnd()
                 }
 
                 override fun onTransitionAnimationProgress(
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
index 679c969..cc55df1 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
@@ -31,12 +31,17 @@
 import androidx.annotation.VisibleForTesting
 import com.android.app.animation.Interpolators.LINEAR
 import com.android.systemui.shared.Flags.returnAnimationFrameworkLibrary
+import java.util.concurrent.Executor
 import kotlin.math.roundToInt
 
 private const val TAG = "TransitionAnimator"
 
 /** A base class to animate a window (activity or dialog) launch to or return from a view . */
-class TransitionAnimator(private val timings: Timings, private val interpolators: Interpolators) {
+class TransitionAnimator(
+    private val mainExecutor: Executor,
+    private val timings: Timings,
+    private val interpolators: Interpolators,
+) {
     companion object {
         internal const val DEBUG = false
         private val SRC_MODE = PorterDuffXfermode(PorterDuff.Mode.SRC)
@@ -351,11 +356,27 @@
                     if (DEBUG) {
                         Log.d(TAG, "Animation ended")
                     }
-                    controller.onTransitionAnimationEnd(isExpandingFullyAbove)
-                    transitionContainerOverlay.remove(windowBackgroundLayer)
 
-                    if (moveBackgroundLayerWhenAppVisibilityChanges && controller.isLaunching) {
-                        openingWindowSyncViewOverlay?.remove(windowBackgroundLayer)
+                    // onAnimationEnd is called at the end of the animation, on a Choreographer
+                    // animation tick. During dialog launches, the following calls will move the
+                    // animated content from the dialog overlay back to its original position, and
+                    // this change must be reflected in the next frame given that we then sync the
+                    // next frame of both the content and dialog ViewRoots. During SysUI activity
+                    // launches, we will instantly collapse the shade at the end of the transition.
+                    // However, if those are rendered by Compose, whose compositions are also
+                    // scheduled on a Choreographer frame, any state change made *right now* won't
+                    // be reflected in the next frame given that a Choreographer frame can't
+                    // schedule another and have it happen in the same frame. So we post the
+                    // forwarded calls to [Controller.onLaunchAnimationEnd] in the main executor,
+                    // leaving this Choreographer frame, ensuring that any state change applied by
+                    // onTransitionAnimationEnd() will be reflected in the same frame.
+                    mainExecutor.execute {
+                        controller.onTransitionAnimationEnd(isExpandingFullyAbove)
+                        transitionContainerOverlay.remove(windowBackgroundLayer)
+
+                        if (moveBackgroundLayerWhenAppVisibilityChanges && controller.isLaunching) {
+                            openingWindowSyncViewOverlay?.remove(windowBackgroundLayer)
+                        }
                     }
                 }
             }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt
index d924d88..92d5c26 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt
@@ -74,6 +74,16 @@
          */
         val isUserInputOngoing: Flow<Boolean>,
     ) : ObservableTransitionState
+
+    fun isIdle(scene: SceneKey?): Boolean {
+        return this is Idle && (scene == null || this.currentScene == scene)
+    }
+
+    fun isTransitioning(from: SceneKey? = null, to: SceneKey? = null): Boolean {
+        return this is Transition &&
+            (from == null || this.fromScene == from) &&
+            (to == null || this.toScene == to)
+    }
 }
 
 /**
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
index 9d6a66d..1f8cb8a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
@@ -90,7 +90,7 @@
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(ParameterizedAndroidJunit4::class)
-class CommunalViewModelTest(flags: FlagsParameterization?) : SysuiTestCase() {
+class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
     @Mock private lateinit var mediaHost: MediaHost
     @Mock private lateinit var user: UserInfo
     @Mock private lateinit var providerInfo: AppWidgetProviderInfo
@@ -111,7 +111,7 @@
     private lateinit var underTest: CommunalViewModel
 
     init {
-        mSetFlagsRule.setFlagsParameterization(flags!!)
+        mSetFlagsRule.setFlagsParameterization(flags)
     }
 
     @Before
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
index 3b6f6a2..f31eb7f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
@@ -58,7 +58,7 @@
 @ExperimentalCoroutinesApi
 @SmallTest
 @RunWith(ParameterizedAndroidJunit4::class)
-class UdfpsKeyguardInteractorTest(flags: FlagsParameterization?) : SysuiTestCase() {
+class UdfpsKeyguardInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
     val kosmos = testKosmos()
     val testScope = kosmos.testScope
     val keyguardRepository = kosmos.fakeKeyguardRepository
@@ -88,7 +88,7 @@
     }
 
     init {
-        mSetFlagsRule.setFlagsParameterization(flags!!)
+        mSetFlagsRule.setFlagsParameterization(flags)
     }
 
     @Before
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt
index f52c66e..cde703b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt
@@ -43,7 +43,7 @@
 @ExperimentalCoroutinesApi
 @SmallTest
 @RunWith(ParameterizedAndroidJunit4::class)
-class AodToLockscreenTransitionViewModelTest(flags: FlagsParameterization?) : SysuiTestCase() {
+class AodToLockscreenTransitionViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
     val kosmos = testKosmos()
     val testScope = kosmos.testScope
     val repository = kosmos.fakeKeyguardTransitionRepository
@@ -60,7 +60,7 @@
     }
 
     init {
-        mSetFlagsRule.setFlagsParameterization(flags!!)
+        mSetFlagsRule.setFlagsParameterization(flags)
     }
 
     @Before
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
index fee18dd..d632936 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
@@ -16,13 +16,15 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
-import androidx.test.ext.junit.runners.AndroidJUnit4
+import android.platform.test.flag.junit.FlagsParameterization
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.bouncer.domain.interactor.mockPrimaryBouncerInteractor
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.BrokenWithSceneContainer
 import com.android.systemui.flags.Flags
+import com.android.systemui.flags.andSceneContainer
 import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -31,29 +33,25 @@
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.shade.data.repository.shadeRepository
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.shadeTestUtil
 import com.android.systemui.statusbar.sysuiStatusBarStateController
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.whenever
 import com.google.common.collect.Range
 import com.google.common.truth.Truth.assertThat
 import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mock
 import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
 
 @SmallTest
-@RunWith(AndroidJUnit4::class)
-class BouncerToGoneFlowsTest : SysuiTestCase() {
-    @Mock private lateinit var shadeInteractor: ShadeInteractor
-
-    private val shadeExpansionStateFlow = MutableStateFlow(0.1f)
+@RunWith(ParameterizedAndroidJunit4::class)
+class BouncerToGoneFlowsTest(flags: FlagsParameterization) : SysuiTestCase() {
 
     private val kosmos =
         testKosmos().apply {
@@ -61,16 +59,31 @@
         }
     private val testScope = kosmos.testScope
     private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
-    private val shadeRepository = kosmos.shadeRepository
     private val sysuiStatusBarStateController = kosmos.sysuiStatusBarStateController
     private val primaryBouncerInteractor = kosmos.mockPrimaryBouncerInteractor
-    private val underTest = kosmos.bouncerToGoneFlows
+
+    private val shadeTestUtil by lazy { kosmos.shadeTestUtil }
+
+    private lateinit var underTest: BouncerToGoneFlows
+
+    companion object {
+        @JvmStatic
+        @Parameters(name = "{0}")
+        fun getParams(): List<FlagsParameterization> {
+            return FlagsParameterization.allCombinationsOf().andSceneContainer()
+        }
+    }
+
+    init {
+        mSetFlagsRule.setFlagsParameterization(flags)
+    }
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
         whenever(primaryBouncerInteractor.willRunDismissFromKeyguard()).thenReturn(false)
         sysuiStatusBarStateController.setLeaveOpenOnKeyguardHide(false)
+        underTest = kosmos.bouncerToGoneFlows
     }
 
     @Test
@@ -79,7 +92,7 @@
             val values by collectValues(underTest.scrimAlpha(500.milliseconds, PRIMARY_BOUNCER))
             runCurrent()
 
-            shadeRepository.setLockscreenShadeExpansion(1f)
+            shadeTestUtil.setLockscreenShadeExpansion(1f)
             whenever(primaryBouncerInteractor.willRunDismissFromKeyguard()).thenReturn(true)
 
             keyguardTransitionRepository.sendTransitionSteps(
@@ -99,12 +112,13 @@
         }
 
     @Test
+    @BrokenWithSceneContainer(339465026)
     fun scrimAlpha_runDimissFromKeyguard_shadeNotExpanded() =
         testScope.runTest {
             val values by collectValues(underTest.scrimAlpha(500.milliseconds, PRIMARY_BOUNCER))
             runCurrent()
 
-            shadeRepository.setLockscreenShadeExpansion(0f)
+            shadeTestUtil.setLockscreenShadeExpansion(0f)
 
             whenever(primaryBouncerInteractor.willRunDismissFromKeyguard()).thenReturn(true)
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index 2e1765a..838b2a7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -60,7 +60,7 @@
 
 @SmallTest
 @RunWith(ParameterizedAndroidJunit4::class)
-class KeyguardRootViewModelTest(flags: FlagsParameterization?) : SysuiTestCase() {
+class KeyguardRootViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val keyguardTransitionRepository by lazy { kosmos.fakeKeyguardTransitionRepository }
@@ -75,7 +75,6 @@
 
     private val viewState = ViewStateAccessor()
 
-    // add to init block
     companion object {
         @JvmStatic
         @Parameters(name = "{0}")
@@ -85,7 +84,7 @@
     }
 
     init {
-        mSetFlagsRule.setFlagsParameterization(flags!!)
+        mSetFlagsRule.setFlagsParameterization(flags)
     }
 
     @Before
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
index ec2cb04..de4b999 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
@@ -47,7 +47,7 @@
 
 @SmallTest
 @RunWith(ParameterizedAndroidJunit4::class)
-class LockscreenContentViewModelTest(flags: FlagsParameterization?) : SysuiTestCase() {
+class LockscreenContentViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
 
     private val kosmos: Kosmos = testKosmos()
 
@@ -62,7 +62,7 @@
     }
 
     init {
-        mSetFlagsRule.setFlagsParameterization(flags!!)
+        mSetFlagsRule.setFlagsParameterization(flags)
     }
 
     @Before
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt
index e3ae3ba..bc381f2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt
@@ -49,7 +49,7 @@
 @ExperimentalCoroutinesApi
 @SmallTest
 @RunWith(ParameterizedAndroidJunit4::class)
-class LockscreenToAodTransitionViewModelTest(flags: FlagsParameterization?) : SysuiTestCase() {
+class LockscreenToAodTransitionViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
     private val kosmos =
         testKosmos().apply {
             fakeFeatureFlagsClassic.apply { set(FULL_SCREEN_USER_SWITCHER, false) }
@@ -73,7 +73,7 @@
     }
 
     init {
-        mSetFlagsRule.setFlagsParameterization(flags!!)
+        mSetFlagsRule.setFlagsParameterization(flags)
     }
 
     @Before
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
index adeb395..9337793 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
@@ -51,7 +51,7 @@
 
 @SmallTest
 @RunWith(ParameterizedAndroidJunit4::class)
-class LockscreenToDreamingTransitionViewModelTest(flags: FlagsParameterization?) : SysuiTestCase() {
+class LockscreenToDreamingTransitionViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
 
     private val kosmos =
         testKosmos().apply {
@@ -73,7 +73,7 @@
     }
 
     init {
-        mSetFlagsRule.setFlagsParameterization(flags!!)
+        mSetFlagsRule.setFlagsParameterization(flags)
     }
 
     @Before
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
index f8da74f..6ce7e88 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
@@ -54,7 +54,7 @@
 
 @SmallTest
 @RunWith(ParameterizedAndroidJunit4::class)
-class LockscreenToOccludedTransitionViewModelTest(flags: FlagsParameterization?) : SysuiTestCase() {
+class LockscreenToOccludedTransitionViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
     private val kosmos =
         testKosmos().apply {
             fakeFeatureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
@@ -76,7 +76,7 @@
     }
 
     init {
-        mSetFlagsRule.setFlagsParameterization(flags!!)
+        mSetFlagsRule.setFlagsParameterization(flags)
     }
 
     @Before
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
index d5df159..58c6817 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
@@ -46,7 +46,7 @@
 @ExperimentalCoroutinesApi
 @SmallTest
 @RunWith(ParameterizedAndroidJunit4::class)
-class LockscreenToPrimaryBouncerTransitionViewModelTest(flags: FlagsParameterization?) :
+class LockscreenToPrimaryBouncerTransitionViewModelTest(flags: FlagsParameterization) :
     SysuiTestCase() {
     private val kosmos =
         testKosmos().apply {
@@ -67,7 +67,7 @@
     }
 
     init {
-        mSetFlagsRule.setFlagsParameterization(flags!!)
+        mSetFlagsRule.setFlagsParameterization(flags)
     }
 
     @Before
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt
index aa0ca18..78c4def 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt
@@ -60,7 +60,7 @@
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(ParameterizedAndroidJunit4::class)
-class ShadeInteractorImplTest(flags: FlagsParameterization?) : SysuiTestCase() {
+class ShadeInteractorImplTest(flags: FlagsParameterization) : SysuiTestCase() {
     val kosmos = testKosmos()
     val testScope = kosmos.testScope
     val configurationRepository by lazy { kosmos.fakeConfigurationRepository }
@@ -85,7 +85,7 @@
     }
 
     init {
-        mSetFlagsRule.setFlagsParameterization(flags!!)
+        mSetFlagsRule.setFlagsParameterization(flags)
     }
 
     @Before
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/startable/ShadeStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/startable/ShadeStartableTest.kt
index 44c9695..cecc70c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/startable/ShadeStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/startable/ShadeStartableTest.kt
@@ -60,7 +60,7 @@
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(ParameterizedAndroidJunit4::class)
-class ShadeStartableTest(flags: FlagsParameterization?) : SysuiTestCase() {
+class ShadeStartableTest(flags: FlagsParameterization) : SysuiTestCase() {
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val shadeInteractor by lazy { kosmos.shadeInteractor }
@@ -80,7 +80,7 @@
     }
 
     init {
-        mSetFlagsRule.setFlagsParameterization(flags!!)
+        mSetFlagsRule.setFlagsParameterization(flags)
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
index d353a62..21dc953 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
@@ -65,7 +65,7 @@
 @SmallTest
 @RunWith(ParameterizedAndroidJunit4::class)
 @TestableLooper.RunWithLooper
-class StatusBarStateControllerImplTest(flags: FlagsParameterization?) : SysuiTestCase() {
+class StatusBarStateControllerImplTest(flags: FlagsParameterization) : SysuiTestCase() {
 
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
@@ -84,7 +84,7 @@
     }
 
     init {
-        mSetFlagsRule.setFlagsParameterization(flags!!)
+        mSetFlagsRule.setFlagsParameterization(flags)
     }
 
     @Before
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
index cbbc4d8..9367a93 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
@@ -59,7 +59,7 @@
 
 @SmallTest
 @RunWith(ParameterizedAndroidJunit4::class)
-class NotificationIconContainerStatusBarViewModelTest(flags: FlagsParameterization?) :
+class NotificationIconContainerStatusBarViewModelTest(flags: FlagsParameterization) :
     SysuiTestCase() {
 
     companion object {
@@ -71,7 +71,7 @@
     }
 
     init {
-        mSetFlagsRule.setFlagsParameterization(flags!!)
+        mSetFlagsRule.setFlagsParameterization(flags)
     }
 
     private val kosmos = testKosmos()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
index 7ac549a..cc5df74 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
@@ -20,12 +20,13 @@
 
 import android.app.NotificationManager.Policy
 import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.FlagsParameterization
 import android.provider.Settings
-import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.Flags
+import com.android.systemui.flags.andSceneContainer
 import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.shared.model.StatusBarState
@@ -33,7 +34,7 @@
 import com.android.systemui.power.data.repository.fakePowerRepository
 import com.android.systemui.power.shared.model.WakefulnessState
 import com.android.systemui.res.R
-import com.android.systemui.shade.data.repository.fakeShadeRepository
+import com.android.systemui.shade.shadeTestUtil
 import com.android.systemui.statusbar.data.repository.fakeRemoteInputRepository
 import com.android.systemui.statusbar.notification.data.repository.FakeHeadsUpRowRepository
 import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
@@ -56,11 +57,13 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
 
 @SmallTest
-@RunWith(AndroidJUnit4::class)
+@RunWith(ParameterizedAndroidJunit4::class)
 @EnableFlags(FooterViewRefactor.FLAG_NAME)
-class NotificationListViewModelTest : SysuiTestCase() {
+class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
     private val kosmos =
         testKosmos().apply {
             fakeFeatureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
@@ -72,16 +75,30 @@
     private val fakeKeyguardRepository = kosmos.fakeKeyguardRepository
     private val fakePowerRepository = kosmos.fakePowerRepository
     private val fakeRemoteInputRepository = kosmos.fakeRemoteInputRepository
-    private val fakeShadeRepository = kosmos.fakeShadeRepository
     private val fakeUserSetupRepository = kosmos.fakeUserSetupRepository
     private val headsUpRepository = kosmos.headsUpNotificationRepository
     private val zenModeRepository = kosmos.zenModeRepository
 
-    val underTest = kosmos.notificationListViewModel
+    private val shadeTestUtil by lazy { kosmos.shadeTestUtil }
+
+    private lateinit var underTest: NotificationListViewModel
+
+    companion object {
+        @JvmStatic
+        @Parameters(name = "{0}")
+        fun getParams(): List<FlagsParameterization> {
+            return FlagsParameterization.allCombinationsOf().andSceneContainer()
+        }
+    }
+
+    init {
+        mSetFlagsRule.setFlagsParameterization(flags)
+    }
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
+        underTest = kosmos.notificationListViewModel
     }
 
     @Test
@@ -163,7 +180,7 @@
             // WHEN has no notifs
             activeNotificationListRepository.setActiveNotifs(count = 0)
             // AND quick settings are expanded
-            fakeShadeRepository.legacyQsFullscreen.value = true
+            shadeTestUtil.setQsFullscreen(true)
             runCurrent()
 
             // THEN empty shade is not visible
@@ -178,9 +195,10 @@
             // WHEN has no notifs
             activeNotificationListRepository.setActiveNotifs(count = 0)
             // AND quick settings are expanded
-            fakeShadeRepository.setQsExpansion(1f)
-            // AND split shade is enabled
+            shadeTestUtil.setQsExpansion(1f)
+            // AND split shade is expanded
             overrideResource(R.bool.config_use_split_notification_shade, true)
+            shadeTestUtil.setShadeExpansion(1f)
             fakeConfigurationController.notifyConfigurationChanged()
             runCurrent()
 
@@ -290,7 +308,7 @@
             activeNotificationListRepository.setActiveNotifs(count = 2)
             // AND shade is open
             fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
-            fakeShadeRepository.setLegacyShadeExpansion(1f)
+            shadeTestUtil.setShadeExpansion(1f)
             runCurrent()
 
             // THEN footer is visible
@@ -306,7 +324,7 @@
             activeNotificationListRepository.setActiveNotifs(count = 2)
             // AND shade is open on lockscreen
             fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED)
-            fakeShadeRepository.setLegacyShadeExpansion(1f)
+            shadeTestUtil.setShadeExpansion(1f)
             runCurrent()
 
             // THEN footer is visible
@@ -337,7 +355,7 @@
             activeNotificationListRepository.setActiveNotifs(count = 2)
             // AND shade is open
             fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
-            fakeShadeRepository.setLegacyShadeExpansion(1f)
+            shadeTestUtil.setShadeExpansion(1f)
             // AND user is not set up
             fakeUserSetupRepository.setUserSetUp(false)
             runCurrent()
@@ -355,7 +373,7 @@
             activeNotificationListRepository.setActiveNotifs(count = 2)
             // AND shade is open
             fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
-            fakeShadeRepository.setLegacyShadeExpansion(1f)
+            shadeTestUtil.setShadeExpansion(1f)
             // AND device is starting to go to sleep
             fakePowerRepository.updateWakefulness(WakefulnessState.STARTING_TO_SLEEP)
             runCurrent()
@@ -373,10 +391,10 @@
             activeNotificationListRepository.setActiveNotifs(count = 2)
             // AND shade is open
             fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
-            fakeShadeRepository.setLegacyShadeExpansion(1f)
+            shadeTestUtil.setShadeExpansion(1f)
             // AND quick settings are expanded
-            fakeShadeRepository.setQsExpansion(1f)
-            fakeShadeRepository.legacyQsFullscreen.value = true
+            shadeTestUtil.setQsExpansion(1f)
+            shadeTestUtil.setQsFullscreen(true)
             runCurrent()
 
             // THEN footer is not visible
@@ -390,11 +408,11 @@
 
             // WHEN has notifs
             activeNotificationListRepository.setActiveNotifs(count = 2)
+            // AND quick settings are expanded
+            shadeTestUtil.setQsExpansion(1f)
             // AND shade is open
             fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
-            fakeShadeRepository.setLegacyShadeExpansion(1f)
-            // AND quick settings are expanded
-            fakeShadeRepository.setQsExpansion(1f)
+            shadeTestUtil.setShadeExpansion(1f)
             // AND split shade is enabled
             overrideResource(R.bool.config_use_split_notification_shade, true)
             fakeConfigurationController.notifyConfigurationChanged()
@@ -413,7 +431,7 @@
             activeNotificationListRepository.setActiveNotifs(count = 2)
             // AND shade is open
             fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
-            fakeShadeRepository.setLegacyShadeExpansion(1f)
+            shadeTestUtil.setShadeExpansion(1f)
             // AND remote input is active
             fakeRemoteInputRepository.isRemoteInputActive.value = true
             runCurrent()
@@ -431,7 +449,7 @@
             activeNotificationListRepository.setActiveNotifs(count = 2)
             // AND shade is open and fully expanded
             fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
-            fakeShadeRepository.setLegacyShadeExpansion(1f)
+            shadeTestUtil.setShadeExpansion(1f)
             runCurrent()
 
             // THEN footer visibility animates
@@ -447,7 +465,7 @@
             activeNotificationListRepository.setActiveNotifs(count = 2)
             // AND we are on the keyguard
             fakeKeyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
-            fakeShadeRepository.setLegacyShadeExpansion(1f)
+            shadeTestUtil.setShadeExpansion(1f)
             runCurrent()
 
             // THEN footer visibility does not animate
@@ -461,7 +479,7 @@
 
             // WHEN shade is closed
             fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
-            fakeShadeRepository.setLegacyShadeExpansion(0f)
+            shadeTestUtil.setShadeExpansion(0f)
             runCurrent()
 
             // THEN footer is hidden
@@ -475,7 +493,7 @@
 
             // WHEN shade is open
             fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
-            fakeShadeRepository.setLegacyShadeExpansion(1f)
+            shadeTestUtil.setShadeExpansion(1f)
             runCurrent()
 
             // THEN footer is hidden
@@ -489,8 +507,8 @@
 
             // WHEN QS partially open
             fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
-            fakeShadeRepository.setQsExpansion(0.5f)
-            fakeShadeRepository.setLegacyShadeExpansion(0.5f)
+            shadeTestUtil.setQsExpansion(0.5f)
+            shadeTestUtil.setShadeExpansion(0.5f)
             runCurrent()
 
             // THEN footer is hidden
@@ -588,7 +606,7 @@
         testScope.runTest {
             val animationsEnabled by collectLastValue(underTest.headsUpAnimationsEnabled)
 
-            fakeShadeRepository.setQsExpansion(0.0f)
+            shadeTestUtil.setQsExpansion(0.0f)
             fakeKeyguardRepository.setKeyguardShowing(false)
             runCurrent()
 
@@ -601,7 +619,7 @@
         testScope.runTest {
             val animationsEnabled by collectLastValue(underTest.headsUpAnimationsEnabled)
 
-            fakeShadeRepository.setQsExpansion(0.0f)
+            shadeTestUtil.setQsExpansion(0.0f)
             fakeKeyguardRepository.setKeyguardShowing(true)
             runCurrent()
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index 2cd295c..f2ce745 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -75,7 +75,7 @@
 @RunWith(ParameterizedAndroidJunit4::class)
 // SharedNotificationContainerViewModel is only bound when FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT is on
 @EnableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
-class SharedNotificationContainerViewModelTest(flags: FlagsParameterization?) : SysuiTestCase() {
+class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
 
     companion object {
         @JvmStatic
@@ -89,7 +89,7 @@
     }
 
     init {
-        mSetFlagsRule.setFlagsParameterization(flags!!)
+        mSetFlagsRule.setFlagsParameterization(flags)
     }
 
     val aodBurnInViewModel = mock(AodBurnInViewModel::class.java)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt
index 8ce5037..63f19fb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt
@@ -249,22 +249,40 @@
 
 
     @Test
-    fun testDelete_showingEntryKeyBecomesPreviousHunKey() {
+    fun testDelete_deleteSecondToLastEntry_showingEntryKeyBecomesPreviousHunKey() {
         mAvalancheController.previousHunKey = ""
 
         // Entry is showing
+        val firstEntry = createHeadsUpEntry(id = 0)
+        mAvalancheController.headsUpEntryShowing = firstEntry
+
+        // There's another entry waiting to show next
+        val secondEntry = createHeadsUpEntry(id = 1)
+        mAvalancheController.addToNext(secondEntry, runnableMock!!)
+
+        // Delete
+        mAvalancheController.delete(firstEntry, runnableMock, "testLabel")
+
+        // Next entry is shown
+        assertThat(mAvalancheController.previousHunKey).isEqualTo(firstEntry.mEntry!!.key)
+    }
+
+    @Test
+    fun testDelete_deleteLastEntry_previousHunKeyCleared() {
+        mAvalancheController.previousHunKey = "key"
+
+        // Nothing waiting to show
+        mAvalancheController.clearNext()
+
+        // One entry is showing
         val showingEntry = createHeadsUpEntry(id = 0)
         mAvalancheController.headsUpEntryShowing = showingEntry
 
-        // There's another entry waiting to show next
-        val nextEntry = createHeadsUpEntry(id = 1)
-        mAvalancheController.addToNext(nextEntry, runnableMock!!)
-
         // Delete
-        mAvalancheController.delete(showingEntry, runnableMock, "testLabel")
+        mAvalancheController.delete(showingEntry, runnableMock!!, "testLabel")
 
         // Next entry is shown
-        assertThat(mAvalancheController.previousHunKey).isEqualTo(showingEntry.mEntry!!.key)
+        assertThat(mAvalancheController.previousHunKey).isEqualTo("");
     }
 
     @Test
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/AccessibilityRepository.kt b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/AccessibilityRepository.kt
index ae9f57f..6032f0b 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/AccessibilityRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/AccessibilityRepository.kt
@@ -18,7 +18,7 @@
 
 import android.view.accessibility.AccessibilityManager
 import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import dagger.Module
 import dagger.Provides
 import kotlinx.coroutines.channels.awaitClose
@@ -29,6 +29,8 @@
 interface AccessibilityRepository {
     /** @see [AccessibilityManager.isTouchExplorationEnabled] */
     val isTouchExplorationEnabled: Flow<Boolean>
+    /** @see [AccessibilityManager.isEnabled] */
+    val isEnabled: Flow<Boolean>
 
     companion object {
         operator fun invoke(a11yManager: AccessibilityManager): AccessibilityRepository =
@@ -47,6 +49,15 @@
                 awaitClose { manager.removeTouchExplorationStateChangeListener(listener) }
             }
             .distinctUntilChanged()
+
+    override val isEnabled: Flow<Boolean> =
+        conflatedCallbackFlow {
+                val listener = AccessibilityManager.AccessibilityStateChangeListener(::trySend)
+                manager.addAccessibilityStateChangeListener(listener)
+                trySend(manager.isEnabled)
+                awaitClose { manager.removeAccessibilityStateChangeListener(listener) }
+            }
+            .distinctUntilChanged()
 }
 
 @Module
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/domain/interactor/AccessibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/accessibility/domain/interactor/AccessibilityInteractor.kt
index 968ce0d..93b624a 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/domain/interactor/AccessibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/domain/interactor/AccessibilityInteractor.kt
@@ -28,6 +28,8 @@
     private val a11yRepo: AccessibilityRepository,
 ) {
     /** @see [android.view.accessibility.AccessibilityManager.isTouchExplorationEnabled] */
-    val isTouchExplorationEnabled: Flow<Boolean>
-        get() = a11yRepo.isTouchExplorationEnabled
+    val isTouchExplorationEnabled: Flow<Boolean> = a11yRepo.isTouchExplorationEnabled
+
+    /** @see [android.view.accessibility.AccessibilityManager.isEnabled] */
+    val isEnabled: Flow<Boolean> = a11yRepo.isEnabled
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
index 5a28f711..9b07675f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
 import com.android.wm.shell.animation.Interpolators
 import javax.inject.Inject
@@ -140,6 +141,8 @@
     }
 
     private fun listenForAlternateBouncerToGone() {
+        // TODO(b/336576536): Check if adaptation for scene framework is needed
+        if (SceneContainerFlag.isEnabled) return
         if (KeyguardWmStateRefactor.isEnabled) {
             // Handled via #dismissAlternateBouncer.
             return
@@ -162,6 +165,8 @@
     }
 
     private fun listenForAlternateBouncerToPrimaryBouncer() {
+        // TODO(b/336576536): Check if adaptation for scene framework is needed
+        if (SceneContainerFlag.isEnabled) return
         scope.launch {
             keyguardInteractor.primaryBouncerShowing
                 .filterRelevantKeyguardStateAnd { isPrimaryBouncerShowing ->
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
index 4d73774..a306954 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
@@ -28,6 +28,7 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
 import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.util.kotlin.Utils.Companion.sample
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
@@ -185,6 +186,7 @@
      * PRIMARY_BOUNCER.
      */
     private fun listenForAodToPrimaryBouncer() {
+        if (SceneContainerFlag.isEnabled) return
         scope.launch("$TAG#listenForAodToPrimaryBouncer") {
             keyguardInteractor.primaryBouncerShowing
                 .filterRelevantKeyguardStateAnd { primaryBouncerShowing -> primaryBouncerShowing }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt
index e738ea4..63294f7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.keyguard.shared.model.DozeStateModel
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
@@ -93,6 +94,8 @@
     }
 
     private fun listenForDreamingLockscreenHostedToPrimaryBouncer() {
+        // TODO(b/336576536): Check if adaptation for scene framework is needed
+        if (SceneContainerFlag.isEnabled) return
         scope.launch {
             keyguardInteractor.primaryBouncerShowing
                 .filterRelevantKeyguardStateAnd { isBouncerShowing -> isBouncerShowing }
@@ -101,6 +104,8 @@
     }
 
     private fun listenForDreamingLockscreenHostedToGone() {
+        // TODO(b/336576536): Check if adaptation for scene framework is needed
+        if (SceneContainerFlag.isEnabled) return
         scope.launch {
             keyguardInteractor.biometricUnlockState
                 .filterRelevantKeyguardStateAnd { biometricUnlockState ->
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
index c952e08..7961b45 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.keyguard.shared.model.DozeStateModel
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
@@ -88,6 +89,8 @@
 
     private fun listenForDreamingToGlanceableHub() {
         if (!communalHub()) return
+        if (SceneContainerFlag.isEnabled)
+            return // TODO(b/336576536): Check if adaptation for scene framework is needed
         scope.launch("$TAG#listenForDreamingToGlanceableHub", mainDispatcher) {
             glanceableHubTransitions.listenForGlanceableHubTransition(
                 transitionOwnerName = TAG,
@@ -175,6 +178,8 @@
     }
 
     private fun listenForDreamingToGoneWhenDismissable() {
+        if (SceneContainerFlag.isEnabled)
+            return // TODO(b/336576536): Check if adaptation for scene framework is needed
         scope.launch {
             keyguardInteractor.isAbleToDream
                 .sampleCombine(
@@ -190,6 +195,8 @@
     }
 
     private fun listenForDreamingToGoneFromBiometricUnlock() {
+        // TODO(b/336576536): Check if adaptation for scene framework is needed
+        if (SceneContainerFlag.isEnabled) return
         scope.launch {
             keyguardInteractor.biometricUnlockState
                 .filterRelevantKeyguardStateAnd { biometricUnlockState ->
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
index faab033..da4e989d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
@@ -28,6 +28,7 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
 import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
 import com.android.systemui.util.kotlin.BooleanFlowOperators.not
 import javax.inject.Inject
@@ -62,6 +63,8 @@
     ) {
 
     override fun start() {
+        // TODO(b/336576536): Check if adaptation for scene framework is needed
+        if (SceneContainerFlag.isEnabled) return
         if (!Flags.communalHub()) {
             return
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
index c2c095b..2b3732f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
 import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
@@ -62,6 +63,8 @@
     ) {
 
     override fun start() {
+        // TODO(b/336576536): Check if adaptation for scene framework is needed
+        if (SceneContainerFlag.isEnabled) return
         listenForGoneToAodOrDozing()
         listenForGoneToDreaming()
         listenForGoneToLockscreenOrHub()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index 56261e0..dad2d96 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -20,6 +20,7 @@
 import android.util.MathUtils
 import com.android.app.animation.Interpolators
 import com.android.app.tracing.coroutines.launch
+import com.android.systemui.Flags
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
@@ -32,6 +33,7 @@
 import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.shade.data.repository.ShadeRepository
 import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
 import java.util.UUID
@@ -150,6 +152,7 @@
     }
 
     private fun listenForLockscreenToPrimaryBouncer() {
+        if (SceneContainerFlag.isEnabled) return
         scope.launch("$TAG#listenForLockscreenToPrimaryBouncer") {
             keyguardInteractor.primaryBouncerShowing
                 .filterRelevantKeyguardStateAnd { isBouncerShowing -> isBouncerShowing }
@@ -174,6 +177,7 @@
 
     /* Starts transitions when manually dragging up the bouncer from the lockscreen. */
     private fun listenForLockscreenToPrimaryBouncerDragging() {
+        if (SceneContainerFlag.isEnabled) return
         var transitionId: UUID? = null
         scope.launch("$TAG#listenForLockscreenToPrimaryBouncerDragging") {
             shadeRepository.legacyShadeExpansion
@@ -280,6 +284,7 @@
     }
 
     private fun listenForLockscreenToGoneDragging() {
+        if (SceneContainerFlag.isEnabled) return
         if (KeyguardWmStateRefactor.isEnabled) {
             // When the refactor is enabled, we no longer use isKeyguardGoingAway.
             scope.launch("$TAG#listenForLockscreenToGoneDragging") {
@@ -337,7 +342,9 @@
      * keyguard transition.
      */
     private fun listenForLockscreenToGlanceableHub() {
-        if (!com.android.systemui.Flags.communalHub()) {
+        // TODO(b/336576536): Check if adaptation for scene framework is needed
+        if (SceneContainerFlag.isEnabled) return
+        if (!Flags.communalHub()) {
             return
         }
         scope.launch(mainDispatcher) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
index e51ba83..9559250 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.util.kotlin.Utils.Companion.sample
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
@@ -89,18 +90,10 @@
                     .filterRelevantKeyguardStateAnd { onTop -> !onTop }
                     .sample(
                         communalInteractor.isIdleOnCommunal,
-                        communalInteractor.showCommunalFromOccluded,
+                        communalInteractor.showCommunalFromOccluded
                     )
                     .collect { (_, isIdleOnCommunal, showCommunalFromOccluded) ->
-                        // Occlusion signals come from the framework, and should interrupt any
-                        // existing transition
-                        val to =
-                            if (isIdleOnCommunal || showCommunalFromOccluded) {
-                                KeyguardState.GLANCEABLE_HUB
-                            } else {
-                                KeyguardState.LOCKSCREEN
-                            }
-                        startTransitionTo(to)
+                        startTransitionToLockscreenOrHub(isIdleOnCommunal, showCommunalFromOccluded)
                     }
             }
         } else {
@@ -115,21 +108,28 @@
                         !isOccluded && isShowing
                     }
                     .collect { (_, _, isIdleOnCommunal, showCommunalFromOccluded) ->
-                        // Occlusion signals come from the framework, and should interrupt any
-                        // existing transition
-                        val to =
-                            if (isIdleOnCommunal || showCommunalFromOccluded) {
-                                KeyguardState.GLANCEABLE_HUB
-                            } else {
-                                KeyguardState.LOCKSCREEN
-                            }
-                        startTransitionTo(to)
+                        startTransitionToLockscreenOrHub(isIdleOnCommunal, showCommunalFromOccluded)
                     }
             }
         }
     }
 
+    private suspend fun FromOccludedTransitionInteractor.startTransitionToLockscreenOrHub(
+        isIdleOnCommunal: Boolean,
+        showCommunalFromOccluded: Boolean,
+    ) {
+        if (isIdleOnCommunal || showCommunalFromOccluded) {
+            // TODO(b/336576536): Check if adaptation for scene framework is needed
+            if (SceneContainerFlag.isEnabled) return
+            startTransitionTo(KeyguardState.GLANCEABLE_HUB)
+        } else {
+            startTransitionTo(KeyguardState.LOCKSCREEN)
+        }
+    }
+
     private fun listenForOccludedToGone() {
+        // TODO(b/336576536): Check if adaptation for scene framework is needed
+        if (SceneContainerFlag.isEnabled) return
         if (KeyguardWmStateRefactor.isEnabled) {
             // We don't think OCCLUDED to GONE is possible. You should always have to go via a
             // *_BOUNCER state to end up GONE. Launching an activity over a dismissable keyguard
@@ -150,10 +150,6 @@
         }
     }
 
-    fun dismissToGone() {
-        scope.launch { startTransitionTo(KeyguardState.GONE) }
-    }
-
     private fun listenForOccludedToAsleep() {
         scope.launch { listenForSleepTransition() }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
index 181a551..53a0c32 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
@@ -28,6 +28,7 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
 import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import com.android.systemui.util.kotlin.Utils.Companion.sample
 import com.android.systemui.util.kotlin.sample
@@ -98,6 +99,8 @@
     }
 
     private fun listenForPrimaryBouncerToLockscreenHubOrOccluded() {
+        // TODO(b/336576536): Check if adaptation for scene framework is needed
+        if (SceneContainerFlag.isEnabled) return
         if (KeyguardWmStateRefactor.isEnabled) {
             scope.launch {
                 keyguardInteractor.primaryBouncerShowing
@@ -158,10 +161,14 @@
     }
 
     private fun listenForPrimaryBouncerToAsleep() {
+        // TODO(b/336576536): Check if adaptation for scene framework is needed
+        if (SceneContainerFlag.isEnabled) return
         scope.launch { listenForSleepTransition() }
     }
 
     private fun listenForPrimaryBouncerToDreamingLockscreenHosted() {
+        // TODO(b/336576536): Check if adaptation for scene framework is needed
+        if (SceneContainerFlag.isEnabled) return
         scope.launch {
             keyguardInteractor.primaryBouncerShowing
                 .sample(keyguardInteractor.isActiveDreamLockscreenHosted, ::Pair)
@@ -174,6 +181,8 @@
     }
 
     private fun listenForPrimaryBouncerToGone() {
+        // TODO(b/336576536): Check if adaptation for scene framework is needed
+        if (SceneContainerFlag.isEnabled) return
         if (KeyguardWmStateRefactor.isEnabled) {
             // This is handled in KeyguardSecurityContainerController and
             // StatusBarKeyguardViewManager, which calls the transition interactor to kick off a
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt
index 197221a..fcf67d5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt
@@ -25,6 +25,7 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionInfo
 import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.util.kotlin.sample
 import java.util.UUID
 import javax.inject.Inject
@@ -49,6 +50,8 @@
         fromState: KeyguardState,
         toState: KeyguardState,
     ) {
+        // TODO(b/336576536): Check if adaptation for scene framework is needed
+        if (SceneContainerFlag.isEnabled) return
         val toScene =
             if (fromState == KeyguardState.GLANCEABLE_HUB) {
                 CommunalScenes.Blank
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index a18579d..2c05d49 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -28,11 +28,11 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
 import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
 import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
-import com.android.systemui.keyguard.shared.model.KeyguardState.OFF
 import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
 import com.android.systemui.keyguard.shared.model.TransitionInfo
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.util.kotlin.pairwise
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
@@ -356,6 +356,8 @@
      * state.
      */
     fun startDismissKeyguardTransition() {
+        // TODO(b/336576536): Check if adaptation for scene framework is needed
+        if (SceneContainerFlag.isEnabled) return
         when (val startedState = startedKeyguardState.replayCache.last()) {
             LOCKSCREEN -> fromLockscreenTransitionInteractor.get().dismissKeyguard()
             PRIMARY_BOUNCER -> fromPrimaryBouncerTransitionInteractor.get().dismissPrimaryBouncer()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
index bb2eeb7..dc35e43 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
@@ -16,11 +16,16 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
+import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor
+import com.android.systemui.util.kotlin.pairwise
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -42,6 +47,7 @@
     fromBouncerInteractor: FromPrimaryBouncerTransitionInteractor,
     fromAlternateBouncerInteractor: FromAlternateBouncerTransitionInteractor,
     notificationLaunchAnimationInteractor: NotificationLaunchAnimationInteractor,
+    sceneInteractor: SceneInteractor,
 ) {
     private val defaultSurfaceBehindVisibility =
         transitionInteractor.finishedKeyguardState.map(::isSurfaceVisible)
@@ -103,21 +109,42 @@
      * animation. This is used to keep the RemoteAnimationTarget alive until we're done using it.
      */
     val usingKeyguardGoingAwayAnimation: Flow<Boolean> =
-        combine(
-                transitionInteractor.isInTransitionToState(KeyguardState.GONE),
-                transitionInteractor.finishedKeyguardState,
-                surfaceBehindInteractor.isAnimatingSurface,
-                notificationLaunchAnimationInteractor.isLaunchAnimationRunning,
-            ) { isInTransitionToGone, finishedState, isAnimatingSurface, notifLaunchRunning ->
-                // Using the animation if we're animating it directly, or if the
-                // ActivityLaunchAnimator is in the process of animating it.
-                val animationsRunning = isAnimatingSurface || notifLaunchRunning
-                // We may still be animating the surface after the keyguard is fully GONE, since
-                // some animations (like the translation spring) are not tied directly to the
-                // transition step amount.
-                isInTransitionToGone || (finishedState == KeyguardState.GONE && animationsRunning)
-            }
-            .distinctUntilChanged()
+        if (SceneContainerFlag.isEnabled) {
+            combine(
+                    sceneInteractor.transitionState,
+                    surfaceBehindInteractor.isAnimatingSurface,
+                    notificationLaunchAnimationInteractor.isLaunchAnimationRunning,
+                ) { transition, isAnimatingSurface, isLaunchAnimationRunning ->
+                    // Using the animation if we're animating it directly, or if the
+                    // ActivityLaunchAnimator is in the process of animating it.
+                    val isAnyAnimationRunning = isAnimatingSurface || isLaunchAnimationRunning
+                    // We may still be animating the surface after the keyguard is fully GONE, since
+                    // some animations (like the translation spring) are not tied directly to the
+                    // transition step amount.
+                    transition.isTransitioning(to = Scenes.Gone) ||
+                        (isAnyAnimationRunning &&
+                            (transition.isIdle(Scenes.Gone) ||
+                                transition.isTransitioning(from = Scenes.Gone)))
+                }
+                .distinctUntilChanged()
+        } else {
+            combine(
+                    transitionInteractor.isInTransitionToState(KeyguardState.GONE),
+                    transitionInteractor.finishedKeyguardState,
+                    surfaceBehindInteractor.isAnimatingSurface,
+                    notificationLaunchAnimationInteractor.isLaunchAnimationRunning,
+                ) { isInTransitionToGone, finishedState, isAnimatingSurface, notifLaunchRunning ->
+                    // Using the animation if we're animating it directly, or if the
+                    // ActivityLaunchAnimator is in the process of animating it.
+                    val animationsRunning = isAnimatingSurface || notifLaunchRunning
+                    // We may still be animating the surface after the keyguard is fully GONE, since
+                    // some animations (like the translation spring) are not tied directly to the
+                    // transition step amount.
+                    isInTransitionToGone ||
+                        (finishedState == KeyguardState.GONE && animationsRunning)
+                }
+                .distinctUntilChanged()
+        }
 
     /**
      * Whether the lockscreen is visible, from the Window Manager (WM) perspective.
@@ -127,28 +154,44 @@
      * want to know if the AOD/clock/notifs/etc. are visible.
      */
     val lockscreenVisibility: Flow<Boolean> =
-        transitionInteractor.currentKeyguardState
-            .sample(transitionInteractor.startedStepWithPrecedingStep, ::Pair)
-            .map { (currentState, startedWithPrev) ->
-                val startedFromStep = startedWithPrev?.previousValue
-                val startedStep = startedWithPrev?.newValue
-                val returningToGoneAfterCancellation =
-                    startedStep?.to == KeyguardState.GONE &&
-                        startedFromStep?.transitionState == TransitionState.CANCELED &&
-                        startedFromStep.from == KeyguardState.GONE
+        if (SceneContainerFlag.isEnabled) {
+            sceneInteractor.transitionState
+                .pairwise(ObservableTransitionState.Idle(Scenes.Lockscreen))
+                .map { (prevTransitionState, transitionState) ->
+                    val isReturningToGoneAfterCancellation =
+                        prevTransitionState.isTransitioning(from = Scenes.Gone) &&
+                            transitionState.isTransitioning(to = Scenes.Gone)
+                    val isNotOnGone =
+                        !transitionState.isTransitioning(from = Scenes.Gone) &&
+                            !transitionState.isIdle(Scenes.Gone)
 
-                if (!returningToGoneAfterCancellation) {
-                    // By default, apply the lockscreen visibility of the current state.
-                    KeyguardState.lockscreenVisibleInState(currentState)
-                } else {
-                    // If we're transitioning to GONE after a prior canceled transition from GONE,
-                    // then this is the camera launch transition from an asleep state back to GONE.
-                    // We don't want to show the lockscreen since we're aborting the lock and going
-                    // back to GONE.
-                    KeyguardState.lockscreenVisibleInState(KeyguardState.GONE)
+                    isNotOnGone && !isReturningToGoneAfterCancellation
                 }
-            }
-            .distinctUntilChanged()
+                .distinctUntilChanged()
+        } else {
+            transitionInteractor.currentKeyguardState
+                .sample(transitionInteractor.startedStepWithPrecedingStep, ::Pair)
+                .map { (currentState, startedWithPrev) ->
+                    val startedFromStep = startedWithPrev?.previousValue
+                    val startedStep = startedWithPrev?.newValue
+                    val returningToGoneAfterCancellation =
+                        startedStep?.to == KeyguardState.GONE &&
+                            startedFromStep?.transitionState == TransitionState.CANCELED &&
+                            startedFromStep.from == KeyguardState.GONE
+
+                    if (!returningToGoneAfterCancellation) {
+                        // By default, apply the lockscreen visibility of the current state.
+                        KeyguardState.lockscreenVisibleInState(currentState)
+                    } else {
+                        // If we're transitioning to GONE after a prior canceled transition from
+                        // GONE, then this is the camera launch transition from an asleep state back
+                        // to GONE. We don't want to show the lockscreen since we're aborting the
+                        // lock and going back to GONE.
+                        KeyguardState.lockscreenVisibleInState(KeyguardState.GONE)
+                    }
+                }
+                .distinctUntilChanged()
+        }
 
     /**
      * Whether always-on-display (AOD) is visible when the lockscreen is visible, from window
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
index 4f00495..e2b66c5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
@@ -76,7 +76,7 @@
                         view,
                         HapticFeedbackConstants.CONFIRM,
                     )
-                    applicationScope.launch { viewModel.onLongPress() }
+                    applicationScope.launch { viewModel.onUserInteraction() }
                 }
             }
 
@@ -116,6 +116,17 @@
                 launch("$TAG#viewModel.accessibilityDelegateHint") {
                     viewModel.accessibilityDelegateHint.collect { hint ->
                         view.accessibilityHintType = hint
+                        if (hint != DeviceEntryIconView.AccessibilityHintType.NONE) {
+                            view.setOnClickListener {
+                                vibratorHelper.performHapticFeedback(
+                                    view,
+                                    HapticFeedbackConstants.CONFIRM,
+                                )
+                                applicationScope.launch { viewModel.onUserInteraction() }
+                            }
+                        } else {
+                            view.setOnClickListener(null)
+                        }
                     }
                 }
                 launch("$TAG#viewModel.useBackgroundProtection") {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
index 35b2598..200d30c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
@@ -65,12 +65,12 @@
             object : AccessibilityDelegate() {
                 private val accessibilityAuthenticateHint =
                     AccessibilityNodeInfo.AccessibilityAction(
-                        AccessibilityNodeInfoCompat.ACTION_LONG_CLICK,
+                        AccessibilityNodeInfoCompat.ACTION_CLICK,
                         resources.getString(R.string.accessibility_authenticate_hint)
                     )
                 private val accessibilityEnterHint =
                     AccessibilityNodeInfo.AccessibilityAction(
-                        AccessibilityNodeInfoCompat.ACTION_LONG_CLICK,
+                        AccessibilityNodeInfoCompat.ACTION_CLICK,
                         resources.getString(R.string.accessibility_enter_hint)
                     )
                 override fun onInitializeAccessibilityNodeInfo(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
index da2fcc4..53b2697 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
@@ -19,6 +19,7 @@
 import android.animation.FloatEvaluator
 import android.animation.IntEvaluator
 import com.android.keyguard.KeyguardViewController
+import com.android.systemui.accessibility.domain.interactor.AccessibilityInteractor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
@@ -68,6 +69,7 @@
     private val keyguardViewController: Lazy<KeyguardViewController>,
     private val deviceEntryInteractor: DeviceEntryInteractor,
     private val deviceEntrySourceInteractor: DeviceEntrySourceInteractor,
+    private val accessibilityInteractor: AccessibilityInteractor,
     @Application private val scope: CoroutineScope,
 ) {
     val isUdfpsSupported: StateFlow<Boolean> = deviceEntryUdfpsInteractor.isUdfpsSupported
@@ -232,7 +234,8 @@
             }
         }
     val isVisible: Flow<Boolean> = deviceEntryViewAlpha.map { it > 0f }.distinctUntilChanged()
-    val isLongPressEnabled: Flow<Boolean> =
+
+    private val isInteractive: Flow<Boolean> =
         combine(
             iconType,
             isUdfpsSupported,
@@ -244,17 +247,24 @@
                 DeviceEntryIconView.IconType.NONE -> false
             }
         }
-
     val accessibilityDelegateHint: Flow<DeviceEntryIconView.AccessibilityHintType> =
-        combine(iconType, isLongPressEnabled) { deviceEntryStatus, longPressEnabled ->
-            if (longPressEnabled) {
-                deviceEntryStatus.toAccessibilityHintType()
+        accessibilityInteractor.isEnabled.flatMapLatest { touchExplorationEnabled ->
+            if (touchExplorationEnabled) {
+                combine(iconType, isInteractive) { iconType, isInteractive ->
+                    if (isInteractive) {
+                        iconType.toAccessibilityHintType()
+                    } else {
+                        DeviceEntryIconView.AccessibilityHintType.NONE
+                    }
+                }
             } else {
-                DeviceEntryIconView.AccessibilityHintType.NONE
+                flowOf(DeviceEntryIconView.AccessibilityHintType.NONE)
             }
         }
 
-    suspend fun onLongPress() {
+    val isLongPressEnabled: Flow<Boolean> = isInteractive
+
+    suspend fun onUserInteraction() {
         if (SceneContainerFlag.isEnabled) {
             deviceEntryInteractor.attemptDeviceEntry()
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index c17da4b..0524589 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -32,6 +32,7 @@
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpHandler;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
@@ -56,10 +57,10 @@
 import com.android.systemui.statusbar.phone.CentralSurfacesImpl;
 import com.android.systemui.statusbar.phone.ManagedProfileController;
 import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl;
-import com.android.systemui.statusbar.phone.ui.StatusBarIconList;
 import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
 import com.android.systemui.statusbar.phone.ui.StatusBarIconController;
 import com.android.systemui.statusbar.phone.ui.StatusBarIconControllerImpl;
+import com.android.systemui.statusbar.phone.ui.StatusBarIconList;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
 import dagger.Binds;
@@ -209,14 +210,16 @@
     /** */
     @Provides
     @SysUISingleton
-    static ActivityTransitionAnimator provideActivityTransitionAnimator() {
-        return new ActivityTransitionAnimator();
+    static ActivityTransitionAnimator provideActivityTransitionAnimator(
+            @Main Executor mainExecutor) {
+        return new ActivityTransitionAnimator(mainExecutor);
     }
 
     /** */
     @Provides
     @SysUISingleton
-    static DialogTransitionAnimator provideDialogTransitionAnimator(IDreamManager dreamManager,
+    static DialogTransitionAnimator provideDialogTransitionAnimator(@Main Executor mainExecutor,
+            IDreamManager dreamManager,
             KeyguardStateController keyguardStateController,
             Lazy<AlternateBouncerInteractor> alternateBouncerInteractor,
             InteractionJankMonitor interactionJankMonitor,
@@ -243,7 +246,7 @@
             }
         };
         return new DialogTransitionAnimator(
-                callback, interactionJankMonitor, animationFeatureFlags);
+                mainExecutor, callback, interactionJankMonitor, animationFeatureFlags);
     }
 
     /** */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SubscriptionModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SubscriptionModel.kt
index d9d909a..fc54f14 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SubscriptionModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SubscriptionModel.kt
@@ -33,6 +33,18 @@
      */
     val isOpportunistic: Boolean = false,
 
+    /**
+     * True if this subscription **only** supports non-terrestrial networks (NTN) and false
+     * otherwise. (non-terrestrial == satellite)
+     *
+     * Note that we intend to filter these subscriptions out, because these connections are actually
+     * supported by
+     * [com.android.systemui.statusbar.pipeline.satellite.data.DeviceBasedSatelliteRepository]. See
+     * [com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor] for
+     * the filtering.
+     */
+    val isExclusivelyNonTerrestrial: Boolean = false,
+
     /** Subscriptions in the same group may be filtered or treated as a single subscription */
     val groupUuid: ParcelUuid? = null,
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
index 2278597..425c58b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
@@ -23,6 +23,7 @@
 import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
 import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType
+import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
 import kotlinx.coroutines.flow.StateFlow
 
@@ -76,7 +77,17 @@
      */
     val isInService: StateFlow<Boolean>
 
-    /** Reflects [android.telephony.ServiceState.isUsingNonTerrestrialNetwork] */
+    /**
+     * True if this subscription is actively connected to a non-terrestrial network and false
+     * otherwise. Reflects [android.telephony.ServiceState.isUsingNonTerrestrialNetwork].
+     *
+     * Notably: This value reflects that this subscription is **currently** using a non-terrestrial
+     * network, because some subscriptions can switch between terrestrial and non-terrestrial
+     * networks. [SubscriptionModel.isExclusivelyNonTerrestrial] reflects whether a subscription is
+     * configured to exclusively connect to non-terrestrial networks. [isNonTerrestrial] can change
+     * during the lifetime of a subscription but [SubscriptionModel.isExclusivelyNonTerrestrial]
+     * will stay constant.
+     */
     val isNonTerrestrial: StateFlow<Boolean>
 
     /** True if [android.telephony.SignalStrength] told us that this connection is using GSM */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
index 5d91ef3..0073e9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
@@ -424,6 +424,7 @@
         SubscriptionModel(
             subscriptionId = subscriptionId,
             isOpportunistic = isOpportunistic,
+            isExclusivelyNonTerrestrial = isOnlyNonTerrestrialNetwork,
             groupUuid = groupUuid,
             carrierName = carrierName.toString(),
             profileClass = profileClass,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
index d555c47..91d7ca6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
@@ -172,21 +172,33 @@
     private val unfilteredSubscriptions: Flow<List<SubscriptionModel>> =
         mobileConnectionsRepo.subscriptions
 
-    /**
-     * Any filtering that we can do based purely on the info of each subscription. Currently this
-     * only applies the ProfileClass-based filter, but if we need other they can go here
-     */
+    /** Any filtering that we can do based purely on the info of each subscription individually. */
     private val subscriptionsBasedFilteredSubs =
-        unfilteredSubscriptions.map { subs -> applyProvisioningFilter(subs) }.distinctUntilChanged()
+        unfilteredSubscriptions
+            .map { it.filterBasedOnProvisioning().filterBasedOnNtn() }
+            .distinctUntilChanged()
 
-    private fun applyProvisioningFilter(subs: List<SubscriptionModel>): List<SubscriptionModel> =
+    private fun List<SubscriptionModel>.filterBasedOnProvisioning(): List<SubscriptionModel> =
         if (!featureFlagsClassic.isEnabled(FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS)) {
-            subs
+            this
         } else {
-            subs.filter { it.profileClass != PROFILE_CLASS_PROVISIONING }
+            this.filter { it.profileClass != PROFILE_CLASS_PROVISIONING }
         }
 
     /**
+     * Subscriptions that exclusively support non-terrestrial networks should **never** directly
+     * show any iconography in the status bar. These subscriptions only exist to provide a backing
+     * for the device-based satellite connections, and the iconography for those connections are
+     * already being handled in
+     * [com.android.systemui.statusbar.pipeline.satellite.data.DeviceBasedSatelliteRepository]. We
+     * need to filter out those subscriptions here so we guarantee the subscription never turns into
+     * an icon. See b/336881301.
+     */
+    private fun List<SubscriptionModel>.filterBasedOnNtn(): List<SubscriptionModel> {
+        return this.filter { !it.isExclusivelyNonTerrestrial }
+    }
+
+    /**
      * Generally, SystemUI wants to show iconography for each subscription that is listed by
      * [SubscriptionManager]. However, in the case of opportunistic subscriptions, we want to only
      * show a single representation of the pair of subscriptions. The docs define opportunistic as:
@@ -204,12 +216,8 @@
                 subscriptionsBasedFilteredSubs,
                 mobileConnectionsRepo.activeMobileDataSubscriptionId,
                 connectivityRepository.vcnSubId,
-            ) { unfilteredSubs, activeId, vcnSubId ->
-                filterSubsBasedOnOpportunistic(
-                    unfilteredSubs,
-                    activeId,
-                    vcnSubId,
-                )
+            ) { preFilteredSubs, activeId, vcnSubId ->
+                filterSubsBasedOnOpportunistic(preFilteredSubs, activeId, vcnSubId)
             }
             .distinctUntilChanged()
             .logDiffsForTable(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
index 2670a95..fa8a7d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
@@ -253,6 +253,7 @@
 
         if (nextList.isEmpty()) {
             log { "NO MORE TO SHOW" }
+            previousHunKey = ""
             return
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
index 41974f4..8e4c155 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
@@ -46,7 +46,8 @@
 @RunWithLooper
 class ActivityTransitionAnimatorTest : SysuiTestCase() {
     private val transitionContainer = LinearLayout(mContext)
-    private val testTransitionAnimator = fakeTransitionAnimator()
+    private val mainExecutor = context.mainExecutor
+    private val testTransitionAnimator = fakeTransitionAnimator(mainExecutor)
     @Mock lateinit var callback: ActivityTransitionAnimator.Callback
     @Mock lateinit var listener: ActivityTransitionAnimator.Listener
     @Spy private val controller = TestTransitionAnimatorController(transitionContainer)
@@ -59,9 +60,10 @@
     fun setup() {
         activityTransitionAnimator =
             ActivityTransitionAnimator(
+                mainExecutor,
                 testTransitionAnimator,
                 testTransitionAnimator,
-                disableWmTimeout = true
+                disableWmTimeout = true,
             )
         activityTransitionAnimator.callback = callback
         activityTransitionAnimator.addListener(listener)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogTransitionAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogTransitionAnimatorTest.kt
index d84a578..e14762cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogTransitionAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogTransitionAnimatorTest.kt
@@ -156,6 +156,7 @@
     fun testActivityLaunchWhenLockedWithoutAlternateAuth() {
         val dialogTransitionAnimator =
                 fakeDialogTransitionAnimator(
+                        mainExecutor = mContext.mainExecutor,
                         isUnlocked = false,
                         isShowingAlternateAuthOnUnlock = false,
                         interactionJankMonitor = kosmos.interactionJankMonitor)
@@ -166,6 +167,7 @@
     @Test
     fun testActivityLaunchWhenLockedWithAlternateAuth() {
         val dialogTransitionAnimator = fakeDialogTransitionAnimator(
+                mainExecutor = mContext.mainExecutor,
                 isUnlocked = false,
                 isShowingAlternateAuthOnUnlock = true,
                 interactionJankMonitor = kosmos.interactionJankMonitor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt
index e64df90..259ece9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt
@@ -25,6 +25,8 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.activity.EmptyTestActivity
+import com.android.systemui.concurrency.fakeExecutor
+import com.android.systemui.kosmos.Kosmos
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -58,9 +60,11 @@
             )
     }
 
+    private val kosmos = Kosmos()
     private val pathManager = GoldenPathManager(context, GOLDENS_PATH, pathConfig = PathConfig())
     private val transitionAnimator =
         TransitionAnimator(
+            kosmos.fakeExecutor,
             ActivityTransitionAnimator.TIMINGS,
             ActivityTransitionAnimator.INTERPOLATORS
         )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DefaultUdfpsTouchOverlayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DefaultUdfpsTouchOverlayViewModelTest.kt
index 5caa146..0d01472 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DefaultUdfpsTouchOverlayViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DefaultUdfpsTouchOverlayViewModelTest.kt
@@ -16,97 +16,95 @@
 
 package com.android.systemui.biometrics.ui.viewmodel
 
+import android.platform.test.flag.junit.FlagsParameterization
 import androidx.test.filters.SmallTest
-import com.android.systemui.SysUITestComponent
-import com.android.systemui.SysUITestModule
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.TestMocksModule
-import com.android.systemui.biometrics.domain.BiometricsDomainLayerModule
-import com.android.systemui.collectLastValue
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FakeFeatureFlagsClassicModule
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.BrokenWithSceneContainer
 import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.flags.parameterizeSceneContainerFlag
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.shared.model.StatusBarState
-import com.android.systemui.runCurrent
-import com.android.systemui.runTest
-import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.shade.shadeTestUtil
 import com.android.systemui.statusbar.phone.SystemUIDialogManager
-import com.android.systemui.user.domain.UserDomainLayerModule
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.statusbar.phone.systemUIDialogManager
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
-import dagger.BindsInstance
-import dagger.Component
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.ArgumentCaptor
 import org.mockito.Captor
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
-class DefaultUdfpsTouchOverlayViewModelTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+class DefaultUdfpsTouchOverlayViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
+
+    private val kosmos =
+        testKosmos().apply {
+            fakeFeatureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, true) }
+        }
+    private val testScope = kosmos.testScope
+
     @Captor
     private lateinit var sysuiDialogListenerCaptor: ArgumentCaptor<SystemUIDialogManager.Listener>
-    private var systemUIDialogManager: SystemUIDialogManager = mock()
+    private var systemUIDialogManager = kosmos.systemUIDialogManager
+    private val keyguardRepository = kosmos.fakeKeyguardRepository
+
+    private val shadeTestUtil by lazy { kosmos.shadeTestUtil }
+
+    private lateinit var underTest: DefaultUdfpsTouchOverlayViewModel
+
+    companion object {
+        @JvmStatic
+        @Parameters(name = "{0}")
+        fun getParams(): List<FlagsParameterization> {
+            return parameterizeSceneContainerFlag()
+        }
+    }
+
+    init {
+        mSetFlagsRule.setFlagsParameterization(flags)
+    }
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
+        underTest =
+            DefaultUdfpsTouchOverlayViewModel(
+                kosmos.shadeInteractor,
+                systemUIDialogManager,
+            )
     }
 
-    @SysUISingleton
-    @Component(
-        modules =
-            [
-                SysUITestModule::class,
-                UserDomainLayerModule::class,
-                BiometricsDomainLayerModule::class,
-            ]
-    )
-    interface TestComponent : SysUITestComponent<DefaultUdfpsTouchOverlayViewModel> {
-        val keyguardRepository: FakeKeyguardRepository
-        val shadeRepository: FakeShadeRepository
-        @Component.Factory
-        interface Factory {
-            fun create(
-                @BindsInstance test: SysuiTestCase,
-                featureFlags: FakeFeatureFlagsClassicModule,
-                mocks: TestMocksModule,
-            ): TestComponent
-        }
-    }
-
-    private fun TestComponent.shadeExpanded(expanded: Boolean) {
+    private fun shadeExpanded(expanded: Boolean) {
         if (expanded) {
-            shadeRepository.setLegacyShadeExpansion(1f)
-            shadeRepository.setLegacyShadeTracking(false)
-            shadeRepository.setLegacyExpandedOrAwaitingInputTransfer(true)
+            shadeTestUtil.setShadeExpansion(1f)
+            shadeTestUtil.setTracking(false)
+            shadeTestUtil.setLegacyExpandedOrAwaitingInputTransfer(true)
         } else {
             keyguardRepository.setStatusBarState(StatusBarState.SHADE)
-            shadeRepository.setLegacyShadeExpansion(0f)
-            shadeRepository.setLegacyShadeTracking(false)
-            shadeRepository.setLegacyExpandedOrAwaitingInputTransfer(false)
+            shadeTestUtil.setShadeExpansion(0f)
+            shadeTestUtil.setTracking(false)
+            shadeTestUtil.setLegacyExpandedOrAwaitingInputTransfer(false)
         }
     }
 
-    private val testComponent: TestComponent =
-        DaggerDefaultUdfpsTouchOverlayViewModelTest_TestComponent.factory()
-            .create(
-                test = this,
-                featureFlags =
-                    FakeFeatureFlagsClassicModule { set(Flags.FULL_SCREEN_USER_SWITCHER, true) },
-                mocks = TestMocksModule(systemUIDialogManager = systemUIDialogManager),
-            )
-
     @Test
+    @BrokenWithSceneContainer(339465026)
     fun shadeNotExpanded_noDialogShowing_shouldHandleTouchesTrue() =
-        testComponent.runTest {
+        testScope.runTest {
             val shouldHandleTouches by collectLastValue(underTest.shouldHandleTouches)
             runCurrent()
 
@@ -120,7 +118,7 @@
 
     @Test
     fun shadeNotExpanded_dialogShowing_shouldHandleTouchesFalse() =
-        testComponent.runTest {
+        testScope.runTest {
             val shouldHandleTouches by collectLastValue(underTest.shouldHandleTouches)
             runCurrent()
 
@@ -134,7 +132,7 @@
 
     @Test
     fun shadeExpanded_noDialogShowing_shouldHandleTouchesFalse() =
-        testComponent.runTest {
+        testScope.runTest {
             val shouldHandleTouches by collectLastValue(underTest.shouldHandleTouches)
             runCurrent()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/ui/viewmodel/UdfpsAccessibilityOverlayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/ui/viewmodel/UdfpsAccessibilityOverlayViewModelTest.kt
index 6a0462b..c39c3fe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/ui/viewmodel/UdfpsAccessibilityOverlayViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/ui/viewmodel/UdfpsAccessibilityOverlayViewModelTest.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.deviceentry.domain.ui.viewmodel
 
-import androidx.test.ext.junit.runners.AndroidJUnit4
+import android.platform.test.flag.junit.FlagsParameterization
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.accessibility.data.repository.fakeAccessibilityRepository
@@ -24,7 +24,9 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
 import com.android.systemui.deviceentry.data.ui.viewmodel.deviceEntryUdfpsAccessibilityOverlayViewModel
+import com.android.systemui.deviceentry.ui.viewmodel.DeviceEntryUdfpsAccessibilityOverlayViewModel
 import com.android.systemui.flags.Flags.FULL_SCREEN_USER_SWITCHER
+import com.android.systemui.flags.andSceneContainer
 import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository
@@ -34,19 +36,22 @@
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.keyguard.ui.viewmodel.fakeDeviceEntryIconViewModelTransition
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.shade.data.repository.fakeShadeRepository
+import com.android.systemui.shade.shadeTestUtil
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
+import org.junit.Before
 import org.junit.runner.RunWith
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
 
 @ExperimentalCoroutinesApi
 @SmallTest
-@RunWith(AndroidJUnit4::class)
-class UdfpsAccessibilityOverlayViewModelTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+class UdfpsAccessibilityOverlayViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
     private val kosmos =
         testKosmos().apply {
             fakeFeatureFlagsClassic.apply { set(FULL_SCREEN_USER_SWITCHER, false) }
@@ -59,8 +64,27 @@
     private val fingerprintPropertyRepository = kosmos.fingerprintPropertyRepository
     private val deviceEntryFingerprintAuthRepository = kosmos.deviceEntryFingerprintAuthRepository
     private val deviceEntryRepository = kosmos.fakeDeviceEntryRepository
-    private val shadeRepository = kosmos.fakeShadeRepository
-    private val underTest = kosmos.deviceEntryUdfpsAccessibilityOverlayViewModel
+
+    private val shadeTestUtil by lazy { kosmos.shadeTestUtil }
+
+    private lateinit var underTest: DeviceEntryUdfpsAccessibilityOverlayViewModel
+
+    companion object {
+        @JvmStatic
+        @Parameters(name = "{0}")
+        fun getParams(): List<FlagsParameterization> {
+            return FlagsParameterization.allCombinationsOf().andSceneContainer()
+        }
+    }
+
+    init {
+        mSetFlagsRule.setFlagsParameterization(flags)
+    }
+
+    @Before
+    fun setup() {
+        underTest = kosmos.deviceEntryUdfpsAccessibilityOverlayViewModel
+    }
 
     @Test
     fun visible() =
@@ -142,7 +166,7 @@
         )
 
         // Shade not expanded
-        shadeRepository.qsExpansion.value = 0f
-        shadeRepository.lockscreenShadeExpansion.value = 0f
+        shadeTestUtil.setQsExpansion(0f)
+        shadeTestUtil.setLockscreenShadeExpansion(0f)
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index 1dc58d1..687e91a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.keyguard.domain.interactor
 
 import android.app.StatusBarManager
+import android.platform.test.flag.junit.FlagsParameterization
 import androidx.test.filters.SmallTest
 import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.keyguard.KeyguardSecurityModel
@@ -29,7 +30,9 @@
 import com.android.systemui.communal.domain.interactor.setCommunalAvailable
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.dock.fakeDockManager
+import com.android.systemui.flags.BrokenWithSceneContainer
 import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.andSceneContainer
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.fakeCommandQueue
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
@@ -46,7 +49,8 @@
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
 import com.android.systemui.power.domain.interactor.powerInteractor
-import com.android.systemui.shade.data.repository.fakeShadeRepository
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.shade.shadeTestUtil
 import com.android.systemui.statusbar.commandQueue
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.whenever
@@ -61,13 +65,14 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Mock
 import org.mockito.Mockito.clearInvocations
 import org.mockito.Mockito.reset
 import org.mockito.Mockito.spy
 import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
 
 /**
  * Class for testing user journeys through the interactors. They will all be activated during setup,
@@ -75,8 +80,8 @@
  */
 @ExperimentalCoroutinesApi
 @SmallTest
-@RunWith(JUnit4::class)
-class KeyguardTransitionScenariosTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+class KeyguardTransitionScenariosTest(flags: FlagsParameterization?) : SysuiTestCase() {
     private val kosmos =
         testKosmos().apply {
             fakeKeyguardTransitionRepository = spy(FakeKeyguardTransitionRepository())
@@ -87,7 +92,7 @@
     private val keyguardRepository = kosmos.fakeKeyguardRepository
     private val bouncerRepository = kosmos.fakeKeyguardBouncerRepository
     private var commandQueue = kosmos.fakeCommandQueue
-    private val shadeRepository = kosmos.fakeShadeRepository
+    private val shadeTestUtil by lazy { kosmos.shadeTestUtil }
     private val transitionRepository = kosmos.fakeKeyguardTransitionRepository
     private lateinit var featureFlags: FakeFeatureFlags
 
@@ -112,6 +117,18 @@
     private val communalInteractor = kosmos.communalInteractor
     private val dockManager = kosmos.fakeDockManager
 
+    companion object {
+        @JvmStatic
+        @Parameters(name = "{0}")
+        fun getParams(): List<FlagsParameterization> {
+            return FlagsParameterization.allCombinationsOf().andSceneContainer()
+        }
+    }
+
+    init {
+        mSetFlagsRule.setFlagsParameterization(flags!!)
+    }
+
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
@@ -119,9 +136,11 @@
         whenever(keyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(PIN)
 
         mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB)
-        mSetFlagsRule.disableFlags(
-            Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR,
-        )
+        if (!SceneContainerFlag.isEnabled) {
+            mSetFlagsRule.disableFlags(
+                Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR,
+            )
+        }
         featureFlags = FakeFeatureFlags()
 
         fromLockscreenTransitionInteractor.start()
@@ -210,6 +229,7 @@
         }
 
     @Test
+    @BrokenWithSceneContainer(339465026)
     fun lockscreenToDreaming() =
         testScope.runTest {
             // GIVEN a device that is not dreaming or dozing
@@ -238,6 +258,7 @@
         }
 
     @Test
+    @BrokenWithSceneContainer(339465026)
     fun lockscreenToDreamingLockscreenHosted() =
         testScope.runTest {
             // GIVEN a device that is not dreaming or dozing
@@ -527,6 +548,7 @@
         }
 
     @Test
+    @BrokenWithSceneContainer(339465026)
     fun dozingToGoneWithUnlock() =
         testScope.runTest {
             // GIVEN a prior transition has run to DOZING
@@ -706,6 +728,7 @@
         }
 
     @Test
+    @BrokenWithSceneContainer(339465026)
     fun goneToLockscreen() =
         testScope.runTest {
             // GIVEN a prior transition has run to GONE
@@ -755,6 +778,7 @@
         }
 
     @Test
+    @BrokenWithSceneContainer(339465026)
     fun goneToGlanceableHub() =
         testScope.runTest {
             // GIVEN a prior transition has run to GONE
@@ -897,6 +921,7 @@
         }
 
     @Test
+    @BrokenWithSceneContainer(339465026)
     fun alternateBouncerToGone() =
         testScope.runTest {
             // GIVEN a prior transition has run to ALTERNATE_BOUNCER
@@ -1135,6 +1160,7 @@
         }
 
     @Test
+    @BrokenWithSceneContainer(339465026)
     fun occludedToGone() =
         testScope.runTest {
             // GIVEN a device on lockscreen
@@ -1165,6 +1191,7 @@
         }
 
     @Test
+    @BrokenWithSceneContainer(339465026)
     fun occludedToLockscreen() =
         testScope.runTest {
             // GIVEN a device on lockscreen
@@ -1193,6 +1220,7 @@
         }
 
     @Test
+    @BrokenWithSceneContainer(339465026)
     fun occludedToGlanceableHub() =
         testScope.runTest {
             // GIVEN a device on lockscreen
@@ -1229,6 +1257,7 @@
         }
 
     @Test
+    @BrokenWithSceneContainer(339465026)
     fun occludedToGlanceableHubWhenInitiallyOnHub() =
         testScope.runTest {
             // GIVEN a device on lockscreen and communal is available
@@ -1314,6 +1343,7 @@
         }
 
     @Test
+    @BrokenWithSceneContainer(339465026)
     fun primaryBouncerToOccluded() =
         testScope.runTest {
             // GIVEN a prior transition has run to PRIMARY_BOUNCER
@@ -1339,6 +1369,7 @@
         }
 
     @Test
+    @BrokenWithSceneContainer(339465026)
     fun dozingToOccluded() =
         testScope.runTest {
             // GIVEN a prior transition has run to DOZING
@@ -1364,6 +1395,7 @@
         }
 
     @Test
+    @BrokenWithSceneContainer(339465026)
     fun dreamingToOccluded() =
         testScope.runTest {
             // GIVEN a prior transition has run to DREAMING
@@ -1484,6 +1516,7 @@
         }
 
     @Test
+    @BrokenWithSceneContainer(339465026)
     fun lockscreenToOccluded() =
         testScope.runTest {
             // GIVEN a prior transition has run to LOCKSCREEN
@@ -1507,6 +1540,7 @@
         }
 
     @Test
+    @BrokenWithSceneContainer(339465026)
     fun aodToOccluded() =
         testScope.runTest {
             // GIVEN a prior transition has run to AOD
@@ -1553,6 +1587,7 @@
         }
 
     @Test
+    @BrokenWithSceneContainer(339465026)
     fun lockscreenToOccluded_fromCameraGesture() =
         testScope.runTest {
             // GIVEN a prior transition has run to LOCKSCREEN
@@ -1586,6 +1621,7 @@
         }
 
     @Test
+    @BrokenWithSceneContainer(339465026)
     fun lockscreenToPrimaryBouncerDragging() =
         testScope.runTest {
             // GIVEN a prior transition has run to LOCKSCREEN
@@ -1595,8 +1631,8 @@
             // GIVEN the keyguard is showing locked
             keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
             runCurrent()
-            shadeRepository.setLegacyShadeTracking(true)
-            shadeRepository.setLegacyShadeExpansion(.9f)
+            shadeTestUtil.setTracking(true)
+            shadeTestUtil.setShadeExpansion(.9f)
             runCurrent()
 
             // THEN a transition from LOCKSCREEN => PRIMARY_BOUNCER should occur
@@ -1613,8 +1649,8 @@
             // WHEN the user stops dragging and shade is back to expanded
             clearInvocations(transitionRepository)
             runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.PRIMARY_BOUNCER)
-            shadeRepository.setLegacyShadeTracking(false)
-            shadeRepository.setLegacyShadeExpansion(1f)
+            shadeTestUtil.setTracking(false)
+            shadeTestUtil.setShadeExpansion(1f)
             runCurrent()
 
             // THEN a transition from LOCKSCREEN => PRIMARY_BOUNCER should occur
@@ -1803,6 +1839,7 @@
         }
 
     @Test
+    @BrokenWithSceneContainer(339465026)
     fun glanceableHubToOccluded() =
         testScope.runTest {
             // GIVEN a prior transition has run to GLANCEABLE_HUB
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
index b1a8dd1..a77169e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
@@ -18,20 +18,29 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.data.repository.sceneContainerRepository
+import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
 import junit.framework.Assert.assertEquals
 import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
+import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -57,14 +66,22 @@
                 .thenReturn(surfaceBehindIsAnimatingFlow)
         }
 
-    private val underTest = kosmos.windowManagerLockscreenVisibilityInteractor
+    private val underTest = lazy { kosmos.windowManagerLockscreenVisibilityInteractor }
     private val testScope = kosmos.testScope
     private val transitionRepository = kosmos.fakeKeyguardTransitionRepository
 
+    @Before
+    fun setUp() {
+        // lazy value needs to be called here otherwise flow collection misbehaves
+        underTest.value
+        kosmos.sceneContainerRepository.setTransitionState(sceneTransitions)
+    }
+
     @Test
+    @DisableSceneContainer
     fun surfaceBehindVisibility_switchesToCorrectFlow() =
         testScope.runTest {
-            val values by collectValues(underTest.surfaceBehindVisibility)
+            val values by collectValues(underTest.value.surfaceBehindVisibility)
 
             // Start on LOCKSCREEN.
             transitionRepository.sendTransitionStep(
@@ -170,9 +187,10 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun testUsingGoingAwayAnimation_duringTransitionToGone() =
         testScope.runTest {
-            val values by collectValues(underTest.usingKeyguardGoingAwayAnimation)
+            val values by collectValues(underTest.value.usingKeyguardGoingAwayAnimation)
 
             // Start on LOCKSCREEN.
             transitionRepository.sendTransitionStep(
@@ -230,9 +248,10 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun testNotUsingGoingAwayAnimation_evenWhenAnimating_ifStateIsNotGone() =
         testScope.runTest {
-            val values by collectValues(underTest.usingKeyguardGoingAwayAnimation)
+            val values by collectValues(underTest.value.usingKeyguardGoingAwayAnimation)
 
             // Start on LOCKSCREEN.
             transitionRepository.sendTransitionStep(
@@ -319,9 +338,10 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun lockscreenVisibility_visibleWhenGone() =
         testScope.runTest {
-            val values by collectValues(underTest.lockscreenVisibility)
+            val values by collectValues(underTest.value.lockscreenVisibility)
 
             // Start on LOCKSCREEN.
             transitionRepository.sendTransitionStep(
@@ -385,9 +405,10 @@
         }
 
     @Test
+    @DisableSceneContainer
     fun testLockscreenVisibility_usesFromState_ifCanceled() =
         testScope.runTest {
-            val values by collectValues(underTest.lockscreenVisibility)
+            val values by collectValues(underTest.value.lockscreenVisibility)
 
             transitionRepository.sendTransitionSteps(
                 from = KeyguardState.LOCKSCREEN,
@@ -486,9 +507,10 @@
      * state during the AOD/isAsleep -> GONE transition is AOD (where lockscreen visibility = true).
      */
     @Test
+    @DisableSceneContainer
     fun testLockscreenVisibility_falseDuringTransitionToGone_fromCanceledGone() =
         testScope.runTest {
-            val values by collectValues(underTest.lockscreenVisibility)
+            val values by collectValues(underTest.value.lockscreenVisibility)
 
             transitionRepository.sendTransitionSteps(
                 from = KeyguardState.LOCKSCREEN,
@@ -587,11 +609,11 @@
             )
         }
 
-    /**  */
     @Test
+    @DisableSceneContainer
     fun testLockscreenVisibility_trueDuringTransitionToGone_fromNotCanceledGone() =
         testScope.runTest {
-            val values by collectValues(underTest.lockscreenVisibility)
+            val values by collectValues(underTest.value.lockscreenVisibility)
 
             transitionRepository.sendTransitionSteps(
                 from = KeyguardState.LOCKSCREEN,
@@ -702,4 +724,109 @@
                 values
             )
         }
+
+    @Test
+    @EnableSceneContainer
+    fun sceneContainer_lockscreenVisibility_visibleWhenNotGone() =
+        testScope.runTest {
+            val lockscreenVisibility by collectLastValue(underTest.value.lockscreenVisibility)
+
+            sceneTransitions.value = lsToGone
+            assertThat(lockscreenVisibility).isTrue()
+
+            sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Gone)
+            assertThat(lockscreenVisibility).isFalse()
+
+            sceneTransitions.value = goneToLs
+            assertThat(lockscreenVisibility).isFalse()
+
+            sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Lockscreen)
+            assertThat(lockscreenVisibility).isTrue()
+        }
+
+    @Test
+    @EnableSceneContainer
+    fun sceneContainer_lockscreenVisibility_notVisibleWhenReturningToGone() =
+        testScope.runTest {
+            val lockscreenVisibility by collectLastValue(underTest.value.lockscreenVisibility)
+
+            sceneTransitions.value = goneToLs
+            assertThat(lockscreenVisibility).isFalse()
+
+            sceneTransitions.value = lsToGone
+            assertThat(lockscreenVisibility).isFalse()
+
+            sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Gone)
+            assertThat(lockscreenVisibility).isFalse()
+
+            sceneTransitions.value = goneToLs
+            assertThat(lockscreenVisibility).isFalse()
+
+            sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Lockscreen)
+            assertThat(lockscreenVisibility).isTrue()
+        }
+
+    @Test
+    @EnableSceneContainer
+    fun sceneContainer_usingGoingAwayAnimation_duringTransitionToGone() =
+        testScope.runTest {
+            val usingKeyguardGoingAwayAnimation by
+                collectLastValue(underTest.value.usingKeyguardGoingAwayAnimation)
+
+            sceneTransitions.value = lsToGone
+            assertThat(usingKeyguardGoingAwayAnimation).isTrue()
+
+            sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Gone)
+            assertThat(usingKeyguardGoingAwayAnimation).isFalse()
+        }
+
+    @Test
+    @EnableSceneContainer
+    fun sceneContainer_usingGoingAwayAnimation_surfaceBehindIsAnimating() =
+        testScope.runTest {
+            val usingKeyguardGoingAwayAnimation by
+                collectLastValue(underTest.value.usingKeyguardGoingAwayAnimation)
+
+            sceneTransitions.value = lsToGone
+            surfaceBehindIsAnimatingFlow.emit(true)
+            assertThat(usingKeyguardGoingAwayAnimation).isTrue()
+
+            sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Gone)
+            assertThat(usingKeyguardGoingAwayAnimation).isTrue()
+
+            sceneTransitions.value = goneToLs
+            assertThat(usingKeyguardGoingAwayAnimation).isTrue()
+
+            surfaceBehindIsAnimatingFlow.emit(false)
+            assertThat(usingKeyguardGoingAwayAnimation).isFalse()
+        }
+
+    companion object {
+        private val progress = MutableStateFlow(0f)
+
+        private val sceneTransitions =
+            MutableStateFlow<ObservableTransitionState>(
+                ObservableTransitionState.Idle(Scenes.Lockscreen)
+            )
+
+        private val lsToGone =
+            ObservableTransitionState.Transition(
+                Scenes.Lockscreen,
+                Scenes.Gone,
+                flowOf(Scenes.Lockscreen),
+                progress,
+                false,
+                flowOf(false)
+            )
+
+        private val goneToLs =
+            ObservableTransitionState.Transition(
+                Scenes.Gone,
+                Scenes.Lockscreen,
+                flowOf(Scenes.Lockscreen),
+                progress,
+                false,
+                flowOf(false)
+            )
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt
index 0bca367..f61ddeb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt
@@ -19,6 +19,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.accessibility.data.repository.fakeAccessibilityRepository
 import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
 import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
 import com.android.systemui.coroutines.collectLastValue
@@ -150,6 +151,51 @@
             assertThat(iconType).isEqualTo(DeviceEntryIconView.IconType.NONE)
         }
 
+    fun accessibilityDelegateHint_accessibilityNotEnabled() =
+        testScope.runTest {
+            val accessibilityDelegateHint by collectLastValue(underTest.accessibilityDelegateHint)
+            kosmos.fakeAccessibilityRepository.isEnabled.value = false
+            assertThat(accessibilityDelegateHint)
+                .isEqualTo(DeviceEntryIconView.AccessibilityHintType.NONE)
+        }
+
+    @Test
+    fun accessibilityDelegateHint_accessibilityEnabled_locked() =
+        testScope.runTest {
+            val accessibilityDelegateHint by collectLastValue(underTest.accessibilityDelegateHint)
+            kosmos.fakeAccessibilityRepository.isEnabled.value = true
+
+            // interactive lock icon
+            keyguardRepository.setKeyguardDismissible(false)
+            fingerprintPropertyRepository.supportsUdfps()
+
+            assertThat(accessibilityDelegateHint)
+                .isEqualTo(DeviceEntryIconView.AccessibilityHintType.AUTHENTICATE)
+
+            // non-interactive lock icon
+            keyguardRepository.setKeyguardDismissible(false)
+            fingerprintPropertyRepository.supportsRearFps()
+
+            assertThat(accessibilityDelegateHint)
+                .isEqualTo(DeviceEntryIconView.AccessibilityHintType.NONE)
+        }
+
+    @Test
+    fun accessibilityDelegateHint_accessibilityEnabled_unlocked() =
+        testScope.runTest {
+            val accessibilityDelegateHint by collectLastValue(underTest.accessibilityDelegateHint)
+            kosmos.fakeAccessibilityRepository.isEnabled.value = true
+
+            // interactive unlock icon
+            keyguardRepository.setKeyguardDismissible(true)
+            fingerprintPropertyRepository.supportsUdfps()
+            advanceTimeBy(UNLOCKED_DELAY_MS * 2) // wait for unlocked delay
+            runCurrent()
+
+            assertThat(accessibilityDelegateHint)
+                .isEqualTo(DeviceEntryIconView.AccessibilityHintType.ENTER)
+        }
+
     private fun deviceEntryIconTransitionAlpha(alpha: Float) {
         deviceEntryIconTransition.setDeviceEntryParentViewAlpha(alpha)
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index 1881a9e..16421a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -88,7 +88,7 @@
 
 @SmallTest
 @RunWith(ParameterizedAndroidJunit4::class)
-class KeyguardBottomAreaViewModelTest(flags: FlagsParameterization?) : SysuiTestCase() {
+class KeyguardBottomAreaViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
 
     @Mock private lateinit var expandable: Expandable
     @Mock private lateinit var burnInHelperWrapper: BurnInHelperWrapper
@@ -115,7 +115,7 @@
     private val kosmos = testKosmos()
 
     init {
-        mSetFlagsRule.setFlagsParameterization(flags!!)
+        mSetFlagsRule.setFlagsParameterization(flags)
     }
 
     @Before
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt
index 0c98cff..768d446 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt
@@ -53,7 +53,7 @@
 
 @SmallTest
 @RunWith(ParameterizedAndroidJunit4::class)
-class KeyguardClockViewModelTest(flags: FlagsParameterization?) : SysuiTestCase() {
+class KeyguardClockViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
     val kosmos = testKosmos()
     val testScope = kosmos.testScope
     val underTest = kosmos.keyguardClockViewModel
@@ -67,7 +67,7 @@
     var faceConfig = ClockFaceConfig()
 
     init {
-        mSetFlagsRule.setFlagsParameterization(flags!!)
+        mSetFlagsRule.setFlagsParameterization(flags)
     }
 
     @Before
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index a867b0f..45d0102 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -102,7 +102,7 @@
 @SmallTest
 @RunWith(ParameterizedAndroidJunit4::class)
 @RunWithLooper(setAsMainLooper = true)
-class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization?) : SysuiTestCase() {
+class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : SysuiTestCase() {
 
     @Mock private lateinit var view: NotificationShadeWindowView
     @Mock private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController
@@ -160,7 +160,7 @@
     private lateinit var featureFlagsClassic: FakeFeatureFlagsClassic
 
     init {
-        mSetFlagsRule.setFlagsParameterization(flags!!)
+        mSetFlagsRule.setFlagsParameterization(flags)
     }
 
     @Before
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt
index 347620a..83ad18b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt
@@ -55,7 +55,7 @@
 @RunWith(ParameterizedAndroidJunit4::class)
 @SmallTest
 @EnableFlags(FooterViewRefactor.FLAG_NAME)
-class FooterViewModelTest(flags: FlagsParameterization?) : SysuiTestCase() {
+class FooterViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
     private val kosmos =
         testKosmos().apply {
             fakeFeatureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
@@ -79,7 +79,7 @@
     }
 
     init {
-        mSetFlagsRule.setFlagsParameterization(flags!!)
+        mSetFlagsRule.setFlagsParameterization(flags)
     }
 
     @Before
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt
index 9b4f931..cb40f72 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt
@@ -66,7 +66,7 @@
 @SmallTest
 @RunWith(ParameterizedAndroidJunit4::class)
 @TestableLooper.RunWithLooper
-class KeyguardBypassControllerTest(flags: FlagsParameterization?) : SysuiTestCase() {
+class KeyguardBypassControllerTest(flags: FlagsParameterization) : SysuiTestCase() {
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val featureFlags = FakeFeatureFlags()
@@ -92,7 +92,7 @@
     }
 
     init {
-        mSetFlagsRule.setFlagsParameterization(flags!!)
+        mSetFlagsRule.setFlagsParameterization(flags)
     }
 
     @Captor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index b5525b1..36df61d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -295,6 +295,50 @@
         }
 
     @Test
+    fun subscriptions_subIsOnlyNtn_modelHasExclusivelyNtnTrue() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.subscriptions)
+
+            val onlyNtnSub =
+                mock<SubscriptionInfo>().also {
+                    whenever(it.isOnlyNonTerrestrialNetwork).thenReturn(true)
+                    whenever(it.subscriptionId).thenReturn(45)
+                    whenever(it.groupUuid).thenReturn(GROUP_1)
+                    whenever(it.carrierName).thenReturn("NTN only")
+                    whenever(it.profileClass).thenReturn(PROFILE_CLASS_UNSET)
+                }
+
+            whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+                .thenReturn(listOf(onlyNtnSub))
+            getSubscriptionCallback().onSubscriptionsChanged()
+
+            assertThat(latest).hasSize(1)
+            assertThat(latest!![0].isExclusivelyNonTerrestrial).isTrue()
+        }
+
+    @Test
+    fun subscriptions_subIsNotOnlyNtn_modelHasExclusivelyNtnFalse() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.subscriptions)
+
+            val notOnlyNtnSub =
+                mock<SubscriptionInfo>().also {
+                    whenever(it.isOnlyNonTerrestrialNetwork).thenReturn(false)
+                    whenever(it.subscriptionId).thenReturn(45)
+                    whenever(it.groupUuid).thenReturn(GROUP_1)
+                    whenever(it.carrierName).thenReturn("NTN only")
+                    whenever(it.profileClass).thenReturn(PROFILE_CLASS_UNSET)
+                }
+
+            whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+                .thenReturn(listOf(notOnlyNtnSub))
+            getSubscriptionCallback().onSubscriptionsChanged()
+
+            assertThat(latest).hasSize(1)
+            assertThat(latest!![0].isExclusivelyNonTerrestrial).isFalse()
+        }
+
+    @Test
     fun testSubscriptions_carrierMergedOnly_listHasCarrierMerged() =
         testScope.runTest {
             val latest by collectLastValue(underTest.subscriptions)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
index 0b14be1..0f9cbfa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
@@ -42,14 +42,11 @@
 import com.google.common.truth.Truth.assertThat
 import java.util.UUID
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import kotlinx.coroutines.test.advanceTimeBy
+import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
-import kotlinx.coroutines.yield
-import org.junit.After
 import org.junit.Before
 import org.junit.Test
 import org.mockito.Mock
@@ -68,7 +65,7 @@
             set(Flags.FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS, true)
         }
 
-    private val testDispatcher = UnconfinedTestDispatcher()
+    private val testDispatcher = StandardTestDispatcher()
     private val testScope = TestScope(testDispatcher)
 
     private val tableLogBuffer =
@@ -113,17 +110,12 @@
             )
     }
 
-    @After fun tearDown() {}
-
     @Test
     fun filteredSubscriptions_default() =
         testScope.runTest {
-            var latest: List<SubscriptionModel>? = null
-            val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.filteredSubscriptions)
 
             assertThat(latest).isEqualTo(listOf<SubscriptionModel>())
-
-            job.cancel()
         }
 
     // Based on the logic from the old pipeline, we'll never filter subs when there are more than 2
@@ -133,12 +125,9 @@
             connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_3_OPP, SUB_4_OPP))
             connectionsRepository.setActiveMobileDataSubscriptionId(SUB_4_ID)
 
-            var latest: List<SubscriptionModel>? = null
-            val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.filteredSubscriptions)
 
             assertThat(latest).isEqualTo(listOf(SUB_1, SUB_3_OPP, SUB_4_OPP))
-
-            job.cancel()
         }
 
     @Test
@@ -146,12 +135,9 @@
         testScope.runTest {
             connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_2))
 
-            var latest: List<SubscriptionModel>? = null
-            val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.filteredSubscriptions)
 
             assertThat(latest).isEqualTo(listOf(SUB_1, SUB_2))
-
-            job.cancel()
         }
 
     @Test
@@ -160,12 +146,9 @@
             connectionsRepository.setSubscriptions(listOf(SUB_3_OPP, SUB_4_OPP))
             connectionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
 
-            var latest: List<SubscriptionModel>? = null
-            val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.filteredSubscriptions)
 
             assertThat(latest).isEqualTo(listOf(SUB_3_OPP, SUB_4_OPP))
-
-            job.cancel()
         }
 
     @Test
@@ -180,12 +163,9 @@
             connectionsRepository.setSubscriptions(listOf(sub1, sub2))
             connectionsRepository.setActiveMobileDataSubscriptionId(SUB_1_ID)
 
-            var latest: List<SubscriptionModel>? = null
-            val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.filteredSubscriptions)
 
             assertThat(latest).isEqualTo(listOf(sub1, sub2))
-
-            job.cancel()
         }
 
     @Test
@@ -202,13 +182,10 @@
             whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
                 .thenReturn(false)
 
-            var latest: List<SubscriptionModel>? = null
-            val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.filteredSubscriptions)
 
             // Filtered subscriptions should show the active one when the config is false
             assertThat(latest).isEqualTo(listOf(sub3))
-
-            job.cancel()
         }
 
     @Test
@@ -225,13 +202,10 @@
             whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
                 .thenReturn(false)
 
-            var latest: List<SubscriptionModel>? = null
-            val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.filteredSubscriptions)
 
             // Filtered subscriptions should show the active one when the config is false
             assertThat(latest).isEqualTo(listOf(sub4))
-
-            job.cancel()
         }
 
     @Test
@@ -248,14 +222,11 @@
             whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
                 .thenReturn(true)
 
-            var latest: List<SubscriptionModel>? = null
-            val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.filteredSubscriptions)
 
             // Filtered subscriptions should show the primary (non-opportunistic) if the config is
             // true
             assertThat(latest).isEqualTo(listOf(sub1))
-
-            job.cancel()
         }
 
     @Test
@@ -272,14 +243,11 @@
             whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
                 .thenReturn(true)
 
-            var latest: List<SubscriptionModel>? = null
-            val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.filteredSubscriptions)
 
             // Filtered subscriptions should show the primary (non-opportunistic) if the config is
             // true
             assertThat(latest).isEqualTo(listOf(sub1))
-
-            job.cancel()
         }
 
     @Test
@@ -297,12 +265,9 @@
             whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
                 .thenReturn(false)
 
-            var latest: List<SubscriptionModel>? = null
-            val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.filteredSubscriptions)
 
             assertThat(latest).isEqualTo(listOf(sub3))
-
-            job.cancel()
         }
 
     @Test
@@ -320,12 +285,9 @@
             whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
                 .thenReturn(false)
 
-            var latest: List<SubscriptionModel>? = null
-            val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.filteredSubscriptions)
 
             assertThat(latest).isEqualTo(listOf(sub1))
-
-            job.cancel()
         }
 
     @Test
@@ -446,313 +408,345 @@
         }
 
     @Test
+    fun filteredSubscriptions_subNotExclusivelyNonTerrestrial_hasSub() =
+        testScope.runTest {
+            val notExclusivelyNonTerrestrialSub =
+                SubscriptionModel(
+                    isExclusivelyNonTerrestrial = false,
+                    subscriptionId = 5,
+                    carrierName = "Carrier 5",
+                    profileClass = PROFILE_CLASS_UNSET,
+                )
+
+            connectionsRepository.setSubscriptions(listOf(notExclusivelyNonTerrestrialSub))
+
+            val latest by collectLastValue(underTest.filteredSubscriptions)
+
+            assertThat(latest).isEqualTo(listOf(notExclusivelyNonTerrestrialSub))
+        }
+
+    @Test
+    fun filteredSubscriptions_subExclusivelyNonTerrestrial_doesNotHaveSub() =
+        testScope.runTest {
+            val exclusivelyNonTerrestrialSub =
+                SubscriptionModel(
+                    isExclusivelyNonTerrestrial = true,
+                    subscriptionId = 5,
+                    carrierName = "Carrier 5",
+                    profileClass = PROFILE_CLASS_UNSET,
+                )
+
+            connectionsRepository.setSubscriptions(listOf(exclusivelyNonTerrestrialSub))
+
+            val latest by collectLastValue(underTest.filteredSubscriptions)
+
+            assertThat(latest).isEmpty()
+        }
+
+    @Test
+    fun filteredSubscription_mixOfExclusivelyNonTerrestrialAndOther_hasOtherSubsOnly() =
+        testScope.runTest {
+            val exclusivelyNonTerrestrialSub =
+                SubscriptionModel(
+                    isExclusivelyNonTerrestrial = true,
+                    subscriptionId = 5,
+                    carrierName = "Carrier 5",
+                    profileClass = PROFILE_CLASS_UNSET,
+                )
+            val otherSub1 =
+                SubscriptionModel(
+                    isExclusivelyNonTerrestrial = false,
+                    subscriptionId = 1,
+                    carrierName = "Carrier 1",
+                    profileClass = PROFILE_CLASS_UNSET,
+                )
+            val otherSub2 =
+                SubscriptionModel(
+                    isExclusivelyNonTerrestrial = false,
+                    subscriptionId = 2,
+                    carrierName = "Carrier 2",
+                    profileClass = PROFILE_CLASS_UNSET,
+                )
+
+            connectionsRepository.setSubscriptions(
+                listOf(otherSub1, exclusivelyNonTerrestrialSub, otherSub2)
+            )
+
+            val latest by collectLastValue(underTest.filteredSubscriptions)
+
+            assertThat(latest).isEqualTo(listOf(otherSub1, otherSub2))
+        }
+
+    @Test
+    fun filteredSubscriptions_exclusivelyNonTerrestrialSub_andOpportunistic_bothFiltersHappen() =
+        testScope.runTest {
+            // Exclusively non-terrestrial sub
+            val exclusivelyNonTerrestrialSub =
+                SubscriptionModel(
+                    isExclusivelyNonTerrestrial = true,
+                    subscriptionId = 5,
+                    carrierName = "Carrier 5",
+                    profileClass = PROFILE_CLASS_UNSET,
+                )
+
+            // Opportunistic subs
+            val (sub3, sub4) =
+                createSubscriptionPair(
+                    subscriptionIds = Pair(SUB_3_ID, SUB_4_ID),
+                    opportunistic = Pair(true, true),
+                    grouped = true,
+                )
+
+            // WHEN both an exclusively non-terrestrial sub and opportunistic sub pair is included
+            connectionsRepository.setSubscriptions(listOf(sub3, sub4, exclusivelyNonTerrestrialSub))
+            connectionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
+
+            val latest by collectLastValue(underTest.filteredSubscriptions)
+
+            // THEN both the only-non-terrestrial sub and the non-active sub are filtered out,
+            // leaving only sub3.
+            assertThat(latest).isEqualTo(listOf(sub3))
+        }
+
+    @Test
     fun activeDataConnection_turnedOn() =
         testScope.runTest {
             CONNECTION_1.setDataEnabled(true)
-            var latest: Boolean? = null
-            val job =
-                underTest.activeDataConnectionHasDataEnabled.onEach { latest = it }.launchIn(this)
+
+            val latest by collectLastValue(underTest.activeDataConnectionHasDataEnabled)
 
             assertThat(latest).isTrue()
-
-            job.cancel()
         }
 
     @Test
     fun activeDataConnection_turnedOff() =
         testScope.runTest {
             CONNECTION_1.setDataEnabled(true)
-            var latest: Boolean? = null
-            val job =
-                underTest.activeDataConnectionHasDataEnabled.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.activeDataConnectionHasDataEnabled)
 
             CONNECTION_1.setDataEnabled(false)
-            yield()
 
             assertThat(latest).isFalse()
-
-            job.cancel()
         }
 
     @Test
     fun activeDataConnection_invalidSubId() =
         testScope.runTest {
-            var latest: Boolean? = null
-            val job =
-                underTest.activeDataConnectionHasDataEnabled.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.activeDataConnectionHasDataEnabled)
 
             connectionsRepository.setActiveMobileDataSubscriptionId(INVALID_SUBSCRIPTION_ID)
-            yield()
 
             // An invalid active subId should tell us that data is off
             assertThat(latest).isFalse()
-
-            job.cancel()
         }
 
     @Test
     fun failedConnection_default_validated_notFailed() =
         testScope.runTest {
-            var latest: Boolean? = null
-            val job = underTest.isDefaultConnectionFailed.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.isDefaultConnectionFailed)
 
             connectionsRepository.mobileIsDefault.value = true
             connectionsRepository.defaultConnectionIsValidated.value = true
-            yield()
 
             assertThat(latest).isFalse()
-
-            job.cancel()
         }
 
     @Test
     fun failedConnection_notDefault_notValidated_notFailed() =
         testScope.runTest {
-            var latest: Boolean? = null
-            val job = underTest.isDefaultConnectionFailed.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.isDefaultConnectionFailed)
 
             connectionsRepository.mobileIsDefault.value = false
             connectionsRepository.defaultConnectionIsValidated.value = false
-            yield()
 
             assertThat(latest).isFalse()
-
-            job.cancel()
         }
 
     @Test
     fun failedConnection_default_notValidated_failed() =
         testScope.runTest {
-            var latest: Boolean? = null
-            val job = underTest.isDefaultConnectionFailed.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.isDefaultConnectionFailed)
 
             connectionsRepository.mobileIsDefault.value = true
             connectionsRepository.defaultConnectionIsValidated.value = false
-            yield()
 
             assertThat(latest).isTrue()
-
-            job.cancel()
         }
 
     @Test
     fun failedConnection_carrierMergedDefault_notValidated_failed() =
         testScope.runTest {
-            var latest: Boolean? = null
-            val job = underTest.isDefaultConnectionFailed.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.isDefaultConnectionFailed)
 
             connectionsRepository.hasCarrierMergedConnection.value = true
             connectionsRepository.defaultConnectionIsValidated.value = false
-            yield()
 
             assertThat(latest).isTrue()
-
-            job.cancel()
         }
 
     /** Regression test for b/275076959. */
     @Test
     fun failedConnection_dataSwitchInSameGroup_notFailed() =
         testScope.runTest {
-            var latest: Boolean? = null
-            val job = underTest.isDefaultConnectionFailed.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.isDefaultConnectionFailed)
 
             connectionsRepository.mobileIsDefault.value = true
             connectionsRepository.defaultConnectionIsValidated.value = true
+            runCurrent()
 
             // WHEN there's a data change in the same subscription group
             connectionsRepository.activeSubChangedInGroupEvent.emit(Unit)
             connectionsRepository.defaultConnectionIsValidated.value = false
+            runCurrent()
 
             // THEN the default connection is *not* marked as failed because of forced validation
             assertThat(latest).isFalse()
-
-            job.cancel()
         }
 
     @Test
     fun failedConnection_dataSwitchNotInSameGroup_isFailed() =
         testScope.runTest {
-            var latestConnectionFailed: Boolean? = null
-            val job =
-                underTest.isDefaultConnectionFailed
-                    .onEach { latestConnectionFailed = it }
-                    .launchIn(this)
+            val latest by collectLastValue(underTest.isDefaultConnectionFailed)
+
             connectionsRepository.mobileIsDefault.value = true
             connectionsRepository.defaultConnectionIsValidated.value = true
+            runCurrent()
 
             // WHEN the connection is invalidated without a activeSubChangedInGroupEvent
             connectionsRepository.defaultConnectionIsValidated.value = false
 
             // THEN the connection is immediately marked as failed
-            assertThat(latestConnectionFailed).isTrue()
-
-            job.cancel()
+            assertThat(latest).isTrue()
         }
 
     @Test
     fun alwaysShowDataRatIcon_configHasTrue() =
         testScope.runTest {
-            var latest: Boolean? = null
-            val job = underTest.alwaysShowDataRatIcon.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.alwaysShowDataRatIcon)
 
             val config = MobileMappings.Config()
             config.alwaysShowDataRatIcon = true
             connectionsRepository.defaultDataSubRatConfig.value = config
-            yield()
 
             assertThat(latest).isTrue()
-
-            job.cancel()
         }
 
     @Test
     fun alwaysShowDataRatIcon_configHasFalse() =
         testScope.runTest {
-            var latest: Boolean? = null
-            val job = underTest.alwaysShowDataRatIcon.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.alwaysShowDataRatIcon)
 
             val config = MobileMappings.Config()
             config.alwaysShowDataRatIcon = false
             connectionsRepository.defaultDataSubRatConfig.value = config
-            yield()
 
             assertThat(latest).isFalse()
-
-            job.cancel()
         }
 
     @Test
     fun alwaysUseCdmaLevel_configHasTrue() =
         testScope.runTest {
-            var latest: Boolean? = null
-            val job = underTest.alwaysUseCdmaLevel.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.alwaysUseCdmaLevel)
 
             val config = MobileMappings.Config()
             config.alwaysShowCdmaRssi = true
             connectionsRepository.defaultDataSubRatConfig.value = config
-            yield()
 
             assertThat(latest).isTrue()
-
-            job.cancel()
         }
 
     @Test
     fun alwaysUseCdmaLevel_configHasFalse() =
         testScope.runTest {
-            var latest: Boolean? = null
-            val job = underTest.alwaysUseCdmaLevel.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.alwaysUseCdmaLevel)
 
             val config = MobileMappings.Config()
             config.alwaysShowCdmaRssi = false
             connectionsRepository.defaultDataSubRatConfig.value = config
-            yield()
 
             assertThat(latest).isFalse()
-
-            job.cancel()
         }
 
     @Test
     fun isSingleCarrier_zeroSubscriptions_false() =
         testScope.runTest {
-            var latest: Boolean? = true
-            val job = underTest.isSingleCarrier.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.isSingleCarrier)
 
             connectionsRepository.setSubscriptions(emptyList())
-            assertThat(latest).isFalse()
 
-            job.cancel()
+            assertThat(latest).isFalse()
         }
 
     @Test
     fun isSingleCarrier_oneSubscription_true() =
         testScope.runTest {
-            var latest: Boolean? = false
-            val job = underTest.isSingleCarrier.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.isSingleCarrier)
 
             connectionsRepository.setSubscriptions(listOf(SUB_1))
-            assertThat(latest).isTrue()
 
-            job.cancel()
+            assertThat(latest).isTrue()
         }
 
     @Test
     fun isSingleCarrier_twoSubscriptions_false() =
         testScope.runTest {
-            var latest: Boolean? = true
-            val job = underTest.isSingleCarrier.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.isSingleCarrier)
 
             connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_2))
-            assertThat(latest).isFalse()
 
-            job.cancel()
+            assertThat(latest).isFalse()
         }
 
     @Test
     fun isSingleCarrier_updates() =
         testScope.runTest {
-            var latest: Boolean? = false
-            val job = underTest.isSingleCarrier.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.isSingleCarrier)
 
             connectionsRepository.setSubscriptions(listOf(SUB_1))
             assertThat(latest).isTrue()
 
             connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_2))
             assertThat(latest).isFalse()
-
-            job.cancel()
         }
 
     @Test
     fun mobileIsDefault_mobileFalseAndCarrierMergedFalse_false() =
         testScope.runTest {
-            var latest: Boolean? = null
-            val job = underTest.mobileIsDefault.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.mobileIsDefault)
 
             connectionsRepository.mobileIsDefault.value = false
             connectionsRepository.hasCarrierMergedConnection.value = false
 
             assertThat(latest).isFalse()
-
-            job.cancel()
         }
 
     @Test
     fun mobileIsDefault_mobileTrueAndCarrierMergedFalse_true() =
         testScope.runTest {
-            var latest: Boolean? = null
-            val job = underTest.mobileIsDefault.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.mobileIsDefault)
 
             connectionsRepository.mobileIsDefault.value = true
             connectionsRepository.hasCarrierMergedConnection.value = false
 
             assertThat(latest).isTrue()
-
-            job.cancel()
         }
 
     /** Regression test for b/272586234. */
     @Test
     fun mobileIsDefault_mobileFalseAndCarrierMergedTrue_true() =
         testScope.runTest {
-            var latest: Boolean? = null
-            val job = underTest.mobileIsDefault.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.mobileIsDefault)
 
             connectionsRepository.mobileIsDefault.value = false
             connectionsRepository.hasCarrierMergedConnection.value = true
 
             assertThat(latest).isTrue()
-
-            job.cancel()
         }
 
     @Test
     fun mobileIsDefault_updatesWhenRepoUpdates() =
         testScope.runTest {
-            var latest: Boolean? = null
-            val job = underTest.mobileIsDefault.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.mobileIsDefault)
 
             connectionsRepository.mobileIsDefault.value = true
             assertThat(latest).isTrue()
@@ -762,8 +756,6 @@
 
             connectionsRepository.hasCarrierMergedConnection.value = true
             assertThat(latest).isTrue()
-
-            job.cancel()
         }
 
     // The data switch tests are mostly testing the [forcingCellularValidation] flow, but that flow
@@ -772,95 +764,79 @@
     @Test
     fun dataSwitch_inSameGroup_validatedMatchesPreviousValue_expiresAfter2s() =
         testScope.runTest {
-            var latestConnectionFailed: Boolean? = null
-            val job =
-                underTest.isDefaultConnectionFailed
-                    .onEach { latestConnectionFailed = it }
-                    .launchIn(this)
+            val latest by collectLastValue(underTest.isDefaultConnectionFailed)
 
             connectionsRepository.mobileIsDefault.value = true
             connectionsRepository.defaultConnectionIsValidated.value = true
+            runCurrent()
 
             // Trigger a data change in the same subscription group that's not yet validated
             connectionsRepository.activeSubChangedInGroupEvent.emit(Unit)
             connectionsRepository.defaultConnectionIsValidated.value = false
+            runCurrent()
 
             // After 1s, the force validation bit is still present, so the connection is not marked
             // as failed
             advanceTimeBy(1000)
-            assertThat(latestConnectionFailed).isFalse()
+            assertThat(latest).isFalse()
 
             // After 2s, the force validation expires so the connection updates to failed
             advanceTimeBy(1001)
-            assertThat(latestConnectionFailed).isTrue()
-
-            job.cancel()
+            assertThat(latest).isTrue()
         }
 
     @Test
     fun dataSwitch_inSameGroup_notValidated_immediatelyMarkedAsFailed() =
         testScope.runTest {
-            var latestConnectionFailed: Boolean? = null
-            val job =
-                underTest.isDefaultConnectionFailed
-                    .onEach { latestConnectionFailed = it }
-                    .launchIn(this)
+            val latest by collectLastValue(underTest.isDefaultConnectionFailed)
 
             connectionsRepository.mobileIsDefault.value = true
             connectionsRepository.defaultConnectionIsValidated.value = false
+            runCurrent()
 
             connectionsRepository.activeSubChangedInGroupEvent.emit(Unit)
 
-            assertThat(latestConnectionFailed).isTrue()
-
-            job.cancel()
+            assertThat(latest).isTrue()
         }
 
     @Test
     fun dataSwitch_loseValidation_thenSwitchHappens_clearsForcedBit() =
         testScope.runTest {
-            var latestConnectionFailed: Boolean? = null
-            val job =
-                underTest.isDefaultConnectionFailed
-                    .onEach { latestConnectionFailed = it }
-                    .launchIn(this)
+            val latest by collectLastValue(underTest.isDefaultConnectionFailed)
 
             // GIVEN the network starts validated
             connectionsRepository.mobileIsDefault.value = true
             connectionsRepository.defaultConnectionIsValidated.value = true
+            runCurrent()
 
             // WHEN a data change happens in the same group
             connectionsRepository.activeSubChangedInGroupEvent.emit(Unit)
 
             // WHEN the validation bit is lost
             connectionsRepository.defaultConnectionIsValidated.value = false
+            runCurrent()
 
             // WHEN another data change happens in the same group
             connectionsRepository.activeSubChangedInGroupEvent.emit(Unit)
 
             // THEN the forced validation bit is still used...
-            assertThat(latestConnectionFailed).isFalse()
+            assertThat(latest).isFalse()
 
             advanceTimeBy(1000)
-            assertThat(latestConnectionFailed).isFalse()
+            assertThat(latest).isFalse()
 
             // ... but expires after 2s
             advanceTimeBy(1001)
-            assertThat(latestConnectionFailed).isTrue()
-
-            job.cancel()
+            assertThat(latest).isTrue()
         }
 
     @Test
     fun dataSwitch_whileAlreadyForcingValidation_resetsClock() =
         testScope.runTest {
-            var latestConnectionFailed: Boolean? = null
-            val job =
-                underTest.isDefaultConnectionFailed
-                    .onEach { latestConnectionFailed = it }
-                    .launchIn(this)
+            val latest by collectLastValue(underTest.isDefaultConnectionFailed)
             connectionsRepository.mobileIsDefault.value = true
             connectionsRepository.defaultConnectionIsValidated.value = true
+            runCurrent()
 
             connectionsRepository.activeSubChangedInGroupEvent.emit(Unit)
 
@@ -869,44 +845,37 @@
             // WHEN another change in same group event happens
             connectionsRepository.activeSubChangedInGroupEvent.emit(Unit)
             connectionsRepository.defaultConnectionIsValidated.value = false
+            runCurrent()
 
             // THEN the forced validation remains for exactly 2 more seconds from now
 
             // 1.500s from second event
             advanceTimeBy(1500)
-            assertThat(latestConnectionFailed).isFalse()
+            assertThat(latest).isFalse()
 
             // 2.001s from the second event
             advanceTimeBy(501)
-            assertThat(latestConnectionFailed).isTrue()
-
-            job.cancel()
+            assertThat(latest).isTrue()
         }
 
     @Test
     fun isForceHidden_repoHasMobileHidden_true() =
         testScope.runTest {
-            var latest: Boolean? = null
-            val job = underTest.isForceHidden.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.isForceHidden)
 
             connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.MOBILE))
 
             assertThat(latest).isTrue()
-
-            job.cancel()
         }
 
     @Test
     fun isForceHidden_repoDoesNotHaveMobileHidden_false() =
         testScope.runTest {
-            var latest: Boolean? = null
-            val job = underTest.isForceHidden.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.isForceHidden)
 
             connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.WIFI))
 
             assertThat(latest).isFalse()
-
-            job.cancel()
         }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
index cfa734a1..ab10bc4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
@@ -47,7 +47,7 @@
 @SmallTest
 @OptIn(ExperimentalCoroutinesApi::class)
 @RunWith(ParameterizedAndroidJunit4::class)
-class KeyguardStatusBarViewModelTest(flags: FlagsParameterization?) : SysuiTestCase() {
+class KeyguardStatusBarViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val keyguardRepository by lazy { kosmos.fakeKeyguardRepository }
@@ -66,7 +66,7 @@
     }
 
     init {
-        mSetFlagsRule.setFlagsParameterization(flags!!)
+        mSetFlagsRule.setFlagsParameterization(flags)
     }
 
     @Before
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 56e5e29..aac3640 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -138,7 +138,6 @@
 import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeAccessibilityRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeAccessibilityRepository.kt
index 4085b1b..923b636 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeAccessibilityRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeAccessibilityRepository.kt
@@ -25,8 +25,9 @@
 @SysUISingleton
 class FakeAccessibilityRepository(
     override val isTouchExplorationEnabled: MutableStateFlow<Boolean>,
+    override val isEnabled: MutableStateFlow<Boolean>,
 ) : AccessibilityRepository {
-    @Inject constructor() : this(MutableStateFlow(false))
+    @Inject constructor() : this(MutableStateFlow(false), MutableStateFlow(false))
 }
 
 @Module
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityTransitionAnimatorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityTransitionAnimatorKosmos.kt
index 66c9afb..b23767e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityTransitionAnimatorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityTransitionAnimatorKosmos.kt
@@ -17,5 +17,13 @@
 package com.android.systemui.animation
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testCase
 
-val Kosmos.activityTransitionAnimator by Kosmos.Fixture { ActivityTransitionAnimator() }
+val Kosmos.activityTransitionAnimator by
+    Kosmos.Fixture {
+        ActivityTransitionAnimator(
+            // The main thread is checked in a bunch of places inside the different transitions
+            // animators, so we have to pass the real main executor here.
+            mainExecutor = testCase.context.mainExecutor,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt
index 77cb167..5a092f3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt
@@ -19,7 +19,13 @@
 import com.android.systemui.jank.interactionJankMonitor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.testCase
 
 val Kosmos.dialogTransitionAnimator by Fixture {
-    fakeDialogTransitionAnimator(interactionJankMonitor = interactionJankMonitor)
+    fakeDialogTransitionAnimator(
+        // The main thread is checked in a bunch of places inside the different transitions
+        // animators, so we have to pass the real main executor here.
+        mainExecutor = testCase.context.mainExecutor,
+        interactionJankMonitor = interactionJankMonitor,
+    )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogTransitionAnimator.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogTransitionAnimator.kt
index 48b72d0..1709329 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogTransitionAnimator.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogTransitionAnimator.kt
@@ -15,17 +15,20 @@
 package com.android.systemui.animation
 
 import com.android.internal.jank.InteractionJankMonitor
-import com.android.systemui.jank.interactionJankMonitor
+import com.android.systemui.dagger.qualifiers.Main
+import java.util.concurrent.Executor
 
 /** A [DialogTransitionAnimator] to be used in tests. */
 @JvmOverloads
 fun fakeDialogTransitionAnimator(
+    @Main mainExecutor: Executor,
     isUnlocked: Boolean = true,
     isShowingAlternateAuthOnUnlock: Boolean = false,
     isPredictiveBackQsDialogAnim: Boolean = false,
     interactionJankMonitor: InteractionJankMonitor,
 ): DialogTransitionAnimator {
     return DialogTransitionAnimator(
+        mainExecutor = mainExecutor,
         callback =
             FakeCallback(
                 isUnlocked = isUnlocked,
@@ -36,7 +39,7 @@
             object : AnimationFeatureFlags {
                 override val isPredictiveBackQsDialogAnim = isPredictiveBackQsDialogAnim
             },
-        transitionAnimator = fakeTransitionAnimator(),
+        transitionAnimator = fakeTransitionAnimator(mainExecutor),
         isForTesting = true,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeTransitionAnimator.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeTransitionAnimator.kt
index bc7ec3f..d07875f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeTransitionAnimator.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeTransitionAnimator.kt
@@ -15,10 +15,12 @@
 package com.android.systemui.animation
 
 import com.android.app.animation.Interpolators
+import com.android.systemui.dagger.qualifiers.Main
+import java.util.concurrent.Executor
 
 /** A [TransitionAnimator] to be used in tests. */
-fun fakeTransitionAnimator(): TransitionAnimator {
-    return TransitionAnimator(TEST_TIMINGS, TEST_INTERPOLATORS)
+fun fakeTransitionAnimator(@Main mainExecutor: Executor): TransitionAnimator {
+    return TransitionAnimator(mainExecutor, TEST_TIMINGS, TEST_INTERPOLATORS)
 }
 
 /**
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt
index 29167d6..b38acc8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.keyguard.domain.interactor
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.statusbar.notification.domain.interactor.notificationLaunchAnimationInteractor
 
 val Kosmos.windowManagerLockscreenVisibilityInteractor by
@@ -29,5 +30,6 @@
             fromBouncerInteractor = fromPrimaryBouncerTransitionInteractor,
             fromAlternateBouncerInteractor = fromAlternateBouncerTransitionInteractor,
             notificationLaunchAnimationInteractor = notificationLaunchAnimationInteractor,
+            sceneInteractor = sceneInteractor,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt
index 58b0ff8..67fa857 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import com.android.systemui.accessibility.domain.interactor.accessibilityInteractor
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
 import com.android.systemui.deviceentry.domain.interactor.deviceEntrySourceInteractor
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
@@ -49,6 +50,7 @@
         keyguardViewController = { statusBarKeyguardViewManager },
         deviceEntryInteractor = deviceEntryInteractor,
         deviceEntrySourceInteractor = deviceEntrySourceInteractor,
+        accessibilityInteractor = accessibilityInteractor,
         scope = testScope.backgroundScope,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtil.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtil.kt
index 38ede44..ea02d0c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtil.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtil.kt
@@ -76,6 +76,16 @@
         delegate.assertFlagValid()
         delegate.programmaticCollapseShade()
     }
+
+    fun setQsFullscreen(qsFullscreen: Boolean) {
+        delegate.assertFlagValid()
+        delegate.setQsFullscreen(qsFullscreen)
+    }
+
+    fun setLegacyExpandedOrAwaitingInputTransfer(legacyExpandedOrAwaitingInputTransfer: Boolean) {
+        delegate.assertFlagValid()
+        delegate.setLegacyExpandedOrAwaitingInputTransfer(legacyExpandedOrAwaitingInputTransfer)
+    }
 }
 
 /** Sets up shade state for tests for a specific value of the scene container flag. */
@@ -103,6 +113,10 @@
 
     /** Sets the shade to half collapsed with no touch input. */
     fun programmaticCollapseShade()
+
+    fun setQsFullscreen(qsFullscreen: Boolean)
+
+    fun setLegacyExpandedOrAwaitingInputTransfer(legacyExpandedOrAwaitingInputTransfer: Boolean)
 }
 
 /** Sets up shade state for tests when the scene container flag is disabled. */
@@ -146,6 +160,14 @@
         shadeRepository.setLegacyShadeExpansion(.5f)
         testScope.runCurrent()
     }
+
+    override fun setQsFullscreen(qsFullscreen: Boolean) {
+        shadeRepository.legacyQsFullscreen.value = true
+    }
+
+    override fun setLegacyExpandedOrAwaitingInputTransfer(expanded: Boolean) {
+        shadeRepository.setLegacyExpandedOrAwaitingInputTransfer(expanded)
+    }
 }
 
 /** Sets up shade state for tests when the scene container flag is enabled. */
@@ -183,6 +205,16 @@
         setTransitionProgress(Scenes.Shade, Scenes.Lockscreen, .5f, false)
     }
 
+    override fun setQsFullscreen(qsFullscreen: Boolean) {
+        setQsExpansion(1f)
+    }
+
+    override fun setLegacyExpandedOrAwaitingInputTransfer(
+        legacyExpandedOrAwaitingInputTransfer: Boolean
+    ) {
+        setShadeExpansion(.1f)
+    }
+
     override fun setLockscreenShadeExpansion(lockscreenShadeExpansion: Float) {
         if (lockscreenShadeExpansion == 0f) {
             setIdleScene(Scenes.Lockscreen)
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 4e02470..483d308 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -859,7 +859,6 @@
                 break;
 
             case android.provider.Settings.System.SCREEN_BRIGHTNESS:
-            case android.provider.Settings.System.SCREEN_BRIGHTNESS_FLOAT:
             case android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE:
                 if (callingUid == Process.SYSTEM_UID) {
                     return false;
diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
index 25885ed..e8faff6 100644
--- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java
+++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
@@ -96,6 +96,7 @@
     interface TransactionReadyListener {
         void onTransactionReady(int mSyncId, SurfaceControl.Transaction transaction);
         default void onTransactionCommitTimeout() {}
+        default void onReadyTimeout() {}
     }
 
     /**
@@ -410,6 +411,7 @@
             if (allFinished && !mReady) {
                 Slog.w(TAG, "Sync group " + mSyncId + " timed-out because not ready. If you see "
                         + "this, please file a bug.");
+                mListener.onReadyTimeout();
             }
             finishNow();
             removeFromDependencies(this);
diff --git a/services/core/java/com/android/server/wm/InputConfigAdapter.java b/services/core/java/com/android/server/wm/InputConfigAdapter.java
index 119fafd..ae6e724 100644
--- a/services/core/java/com/android/server/wm/InputConfigAdapter.java
+++ b/services/core/java/com/android/server/wm/InputConfigAdapter.java
@@ -20,8 +20,6 @@
 import android.view.InputWindowHandle.InputConfigFlags;
 import android.view.WindowManager.LayoutParams;
 
-import java.util.List;
-
 /**
  * A helper to determine the {@link InputConfigFlags} that control the behavior of an input window
  * from several WM attributes.
@@ -47,7 +45,7 @@
      * input configurations that can be mapped directly from a corresponding LayoutParams input
      * feature.
      */
-    private static final List<FlagMapping> INPUT_FEATURE_TO_CONFIG_MAP = List.of(
+    private static final FlagMapping[] INPUT_FEATURE_TO_CONFIG_MAP = {
             new FlagMapping(
                     LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL,
                     InputConfig.NO_INPUT_CHANNEL, false /* inverted */),
@@ -59,7 +57,8 @@
                     InputConfig.SPY, false /* inverted */),
             new FlagMapping(
                     LayoutParams.INPUT_FEATURE_SENSITIVE_FOR_PRIVACY,
-                    InputConfig.SENSITIVE_FOR_PRIVACY, false /* inverted */));
+                    InputConfig.SENSITIVE_FOR_PRIVACY, false /* inverted */)
+    };
 
     @InputConfigFlags
     private static final int INPUT_FEATURE_TO_CONFIG_MASK =
@@ -72,7 +71,7 @@
      * NOTE: The layout params flag {@link LayoutParams#FLAG_NOT_FOCUSABLE} is not handled by this
      * adapter, and must be handled explicitly.
      */
-    private static final List<FlagMapping> LAYOUT_PARAM_FLAG_TO_CONFIG_MAP = List.of(
+    private static final FlagMapping[] LAYOUT_PARAM_FLAG_TO_CONFIG_MAP = {
             new FlagMapping(
                     LayoutParams.FLAG_NOT_TOUCHABLE,
                     InputConfig.NOT_TOUCHABLE, false /* inverted */),
@@ -84,7 +83,8 @@
                     InputConfig.WATCH_OUTSIDE_TOUCH, false /* inverted */),
             new FlagMapping(
                     LayoutParams.FLAG_SLIPPERY,
-                    InputConfig.SLIPPERY, false /* inverted */));
+                    InputConfig.SLIPPERY, false /* inverted */)
+    };
 
     @InputConfigFlags
     private static final int LAYOUT_PARAM_FLAG_TO_CONFIG_MASK =
@@ -119,7 +119,7 @@
     }
 
     @InputConfigFlags
-    private static int applyMapping(int flags, List<FlagMapping> flagToConfigMap) {
+    private static int applyMapping(int flags, FlagMapping[] flagToConfigMap) {
         int inputConfig = 0;
         for (final FlagMapping mapping : flagToConfigMap) {
             final boolean flagSet = (flags & mapping.mFlag) != 0;
@@ -131,7 +131,7 @@
     }
 
     @InputConfigFlags
-    private static int computeMask(List<FlagMapping> flagToConfigMap) {
+    private static int computeMask(FlagMapping[] flagToConfigMap) {
         int mask = 0;
         for (final FlagMapping mapping : flagToConfigMap) {
             mask |= mapping.mInputConfig;
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 1543263..7ec31d5 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1648,14 +1648,6 @@
         }
 
         if (mController.useFullReadyTracking()) {
-            if (mReadyTracker.mMet.isEmpty()) {
-                Slog.e(TAG, "#" + mSyncId + ": No conditions provided");
-            } else {
-                for (int i = 0; i < mReadyTracker.mConditions.size(); ++i) {
-                    Slog.e(TAG, "#" + mSyncId + ": unmet condition at ready: "
-                            + mReadyTracker.mConditions.get(i));
-                }
-            }
             for (int i = 0; i < mReadyTracker.mMet.size(); ++i) {
                 ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "#%d: Met condition: %s",
                         mSyncId, mReadyTracker.mMet.get(i));
@@ -3360,6 +3352,18 @@
         applyReady();
     }
 
+    @Override
+    public void onReadyTimeout() {
+        if (!mController.useFullReadyTracking()) {
+            Slog.e(TAG, "#" + mSyncId + " readiness timeout, used=" + mReadyTrackerOld.mUsed
+                    + " deferReadyDepth=" + mReadyTrackerOld.mDeferReadyDepth
+                    + " group=" + mReadyTrackerOld.mReadyGroups);
+            return;
+        }
+        Slog.e(TAG, "#" + mSyncId + " met conditions: " + mReadyTracker.mMet);
+        Slog.e(TAG, "#" + mSyncId + " unmet conditions: " + mReadyTracker.mConditions);
+    }
+
     /**
      * Represents a condition that must be met before an associated transition can be considered
      * ready.
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 375fc5a..9eb7b22 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -21605,12 +21605,9 @@
                                 == HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER;
             }
 
-            if (Flags.headlessSingleMinTargetSdk()
-                    && mInjector.userManagerIsHeadlessSystemUserMode()
-                    && isSingleUserMode
-                    && !mInjector.isChangeEnabled(
-                            PROVISION_SINGLE_USER_MODE, deviceAdmin.getPackageName(),
-                    caller.getUserId())) {
+            if (Flags.headlessSingleUserFixes() && mInjector.userManagerIsHeadlessSystemUserMode()
+                    && isSingleUserMode && !mInjector.isChangeEnabled(
+                    PROVISION_SINGLE_USER_MODE, deviceAdmin.getPackageName(), caller.getUserId())) {
                 throw new IllegalStateException("Device admin is not targeting Android V.");
             }
 
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
index b155829..6499556 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
@@ -2046,8 +2046,20 @@
                     writer.println("Unknown app ID $appId.")
                 }
             }
+        } else if (args[0] == "--package" && args.size == 2) {
+            val packageName = args[1]
+            service.getState {
+                val packageState = state.externalState.packageStates[packageName]
+                if (packageState != null) {
+                    writer.dumpAppIdState(packageState.appId, state, indexedSetOf(packageName))
+                } else {
+                    writer.println("Unknown package $packageName.")
+                }
+            }
         } else {
-            writer.println("Usage: dumpsys permission [--app-id APP_ID]")
+            writer.println(
+                "Usage: dumpsys permissionmgr [--app-id <APP_ID>] [--package <PACKAGE_NAME>]"
+            )
         }
     }