Merge "Add some logs to make current QS tiles state clearer" into tm-qpr-dev
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index d6586db..d9d7cc9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -185,6 +185,24 @@
     public abstract String getId();
 
     /**
+     * Get disabled reason of device
+     *
+     * @return disabled reason of device
+     */
+    public int getDisableReason() {
+        return -1;
+    }
+
+    /**
+     * Checks if device is has disabled reason
+     *
+     * @return true if device has disabled reason
+     */
+    public boolean hasDisabledReason() {
+        return false;
+    }
+
+    /**
      * Checks if device is suggested device from application
      *
      * @return true if device is suggested device
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
index 4e96dda..cfc38df 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
@@ -33,8 +33,8 @@
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.LocalContentColor
-import androidx.compose.material3.LocalMinimumTouchTargetEnforcement
 import androidx.compose.material3.contentColorFor
+import androidx.compose.material3.minimumInteractiveComponentSize
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.DisposableEffect
@@ -65,21 +65,17 @@
 import androidx.compose.ui.graphics.drawscope.scale
 import androidx.compose.ui.layout.boundsInRoot
 import androidx.compose.ui.layout.findRootCoordinates
-import androidx.compose.ui.layout.layout
 import androidx.compose.ui.layout.onGloballyPositioned
 import androidx.compose.ui.platform.ComposeView
 import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.platform.LocalViewConfiguration
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.dp
 import androidx.lifecycle.ViewTreeLifecycleOwner
 import androidx.lifecycle.ViewTreeViewModelStoreOwner
-import com.android.compose.runtime.movableContentOf
 import com.android.systemui.animation.Expandable
 import com.android.systemui.animation.LaunchAnimator
 import kotlin.math.max
 import kotlin.math.min
-import kotlin.math.roundToInt
 
 /**
  * Create an expandable shape that can launch into an Activity or a Dialog.
@@ -220,21 +216,8 @@
     // If this expandable is expanded when it's being directly clicked on, let's ensure that it has
     // the minimum interactive size followed by all M3 components (48.dp).
     val minInteractiveSizeModifier =
-        if (onClick != null && LocalMinimumTouchTargetEnforcement.current) {
-            // TODO(b/242040009): Replace this by Modifier.minimumInteractiveComponentSize() once
-            // http://aosp/2305511 is available.
-            val minTouchSize = LocalViewConfiguration.current.minimumTouchTargetSize
-            Modifier.layout { measurable, constraints ->
-                // Copied from androidx.compose.material3.InteractiveComponentSize.kt
-                val placeable = measurable.measure(constraints)
-                val width = maxOf(placeable.width, minTouchSize.width.roundToPx())
-                val height = maxOf(placeable.height, minTouchSize.height.roundToPx())
-                layout(width, height) {
-                    val centerX = ((width - placeable.width) / 2f).roundToInt()
-                    val centerY = ((height - placeable.height) / 2f).roundToInt()
-                    placeable.place(centerX, centerY)
-                }
-            }
+        if (onClick != null) {
+            Modifier.minimumInteractiveComponentSize()
         } else {
             Modifier
         }
diff --git a/packages/SystemUI/res/layout/screenshot_static.xml b/packages/SystemUI/res/layout/screenshot_static.xml
index 496eb6e..7e8bc2c 100644
--- a/packages/SystemUI/res/layout/screenshot_static.xml
+++ b/packages/SystemUI/res/layout/screenshot_static.xml
@@ -31,7 +31,7 @@
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="@+id/actions_container"
         app:layout_constraintEnd_toEndOf="@+id/actions_container"
-        app:layout_constraintBottom_toTopOf="@id/screenshot_message_container"/>
+        app:layout_constraintBottom_toTopOf="@id/guideline"/>
     <HorizontalScrollView
         android:id="@+id/actions_container"
         android:layout_width="0dp"
@@ -127,57 +127,28 @@
         app:layout_constraintTop_toTopOf="@id/screenshot_preview"
         android:elevation="7dp"/>
 
+    <androidx.constraintlayout.widget.Guideline
+        android:id="@+id/guideline"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        app:layout_constraintGuide_end="0dp" />
+
     <androidx.constraintlayout.widget.ConstraintLayout
         android:id="@+id/screenshot_message_container"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:layout_marginHorizontal="@dimen/overlay_action_container_margin_horizontal"
-        android:layout_marginVertical="4dp"
+        android:layout_marginTop="4dp"
+        android:layout_marginBottom="@dimen/overlay_action_container_margin_bottom"
         android:paddingHorizontal="@dimen/overlay_action_container_padding_end"
         android:paddingVertical="@dimen/overlay_action_container_padding_vertical"
         android:elevation="4dp"
         android:background="@drawable/action_chip_container_background"
-        android:visibility="gone"
+        android:visibility="invisible"
+        app:layout_constraintTop_toBottomOf="@id/guideline"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintBottom_toBottomOf="parent">
-
-        <ImageView
-            android:id="@+id/screenshot_message_icon"
-            android:layout_width="48dp"
-            android:layout_height="48dp"
-            android:paddingEnd="4dp"
-            android:src="@drawable/ic_work_app_badge"
-            app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintEnd_toStartOf="@id/screenshot_message_content"
-            app:layout_constraintTop_toTopOf="parent"
-            app:layout_constraintBottom_toBottomOf="parent"/>
-
-        <TextView
-            android:id="@+id/screenshot_message_content"
-            android:layout_width="0dp"
-            android:layout_height="wrap_content"
-            android:layout_gravity="start"
-            app:layout_constraintTop_toTopOf="parent"
-            app:layout_constraintBottom_toBottomOf="parent"
-            app:layout_constraintStart_toEndOf="@id/screenshot_message_icon"
-            app:layout_constraintEnd_toStartOf="@id/message_dismiss_button"/>
-
-        <FrameLayout
-            android:id="@+id/message_dismiss_button"
-            android:layout_width="@dimen/overlay_dismiss_button_tappable_size"
-            android:layout_height="@dimen/overlay_dismiss_button_tappable_size"
-            app:layout_constraintStart_toEndOf="@id/screenshot_message_content"
-            app:layout_constraintEnd_toEndOf="parent"
-            app:layout_constraintTop_toTopOf="parent"
-            app:layout_constraintBottom_toBottomOf="parent"
-            android:contentDescription="@string/screenshot_dismiss_work_profile">
-            <ImageView
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:layout_margin="@dimen/overlay_dismiss_button_margin"
-                android:src="@drawable/overlay_cancel"/>
-        </FrameLayout>
-
+        >
     </androidx.constraintlayout.widget.ConstraintLayout>
 </com.android.systemui.screenshot.DraggableConstraintLayout>
diff --git a/packages/SystemUI/res/layout/screenshot_work_profile_first_run.xml b/packages/SystemUI/res/layout/screenshot_work_profile_first_run.xml
new file mode 100644
index 0000000..c794d91
--- /dev/null
+++ b/packages/SystemUI/res/layout/screenshot_work_profile_first_run.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<merge
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+    <ImageView
+        android:id="@+id/screenshot_message_icon"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
+        android:paddingEnd="4dp"
+        android:src="@drawable/ic_work_app_badge"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toStartOf="@id/screenshot_message_content"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"/>
+
+    <TextView
+        android:id="@+id/screenshot_message_content"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_gravity="start"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toEndOf="@id/screenshot_message_icon"
+        app:layout_constraintEnd_toStartOf="@id/message_dismiss_button"/>
+
+    <FrameLayout
+        android:id="@+id/message_dismiss_button"
+        android:layout_width="@dimen/overlay_dismiss_button_tappable_size"
+        android:layout_height="@dimen/overlay_dismiss_button_tappable_size"
+        app:layout_constraintStart_toEndOf="@id/screenshot_message_content"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        android:contentDescription="@string/screenshot_dismiss_work_profile">
+        <ImageView
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_margin="@dimen/overlay_dismiss_button_margin"
+            android:src="@drawable/overlay_cancel"/>
+    </FrameLayout>
+</merge>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index dfc0150..f3bef92 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -337,7 +337,7 @@
     <!-- Used for both start and bottom margin of the preview, relative to the action container -->
     <dimen name="overlay_preview_container_margin">8dp</dimen>
     <dimen name="overlay_action_container_margin_horizontal">8dp</dimen>
-    <dimen name="overlay_action_container_margin_bottom">4dp</dimen>
+    <dimen name="overlay_action_container_margin_bottom">6dp</dimen>
     <dimen name="overlay_bg_protection_height">242dp</dimen>
     <dimen name="overlay_action_container_corner_radius">18dp</dimen>
     <dimen name="overlay_action_container_padding_vertical">4dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index e60835c..5607c63 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2428,6 +2428,8 @@
     <string name="media_output_group_title_speakers_and_displays">Speakers &amp; Displays</string>
     <!-- Title for Suggested Devices group. [CHAR LIMIT=NONE] -->
     <string name="media_output_group_title_suggested_device">Suggested Devices</string>
+    <!-- Sub status indicates device need premium account. [CHAR LIMIT=NONE] -->
+    <string name="media_output_status_require_premium">Requires premium account</string>
 
     <!-- Media Output Broadcast Dialog -->
     <!-- Title for Broadcast First Notify Dialog [CHAR LIMIT=60] -->
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 1680b47..3a940e9 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -54,7 +54,6 @@
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.launch
-import java.io.PrintWriter
 import java.util.Locale
 import java.util.TimeZone
 import java.util.concurrent.Executor
@@ -309,15 +308,6 @@
             resources.getDimensionPixelSize(R.dimen.large_clock_text_size).toFloat())
     }
 
-    /**
-     * Dump information for debugging
-     */
-    fun dump(pw: PrintWriter) {
-        pw.println(this)
-        clock?.dump(pw)
-        regionSampler?.dump(pw)
-    }
-
     @VisibleForTesting
     internal fun listenForDozeAmount(scope: CoroutineScope): Job {
         return scope.launch {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 7f1f941..8684019 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -45,6 +45,7 @@
 import com.android.systemui.plugins.log.LogLevel;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.shared.clocks.ClockRegistry;
+import com.android.systemui.shared.regionsampling.RegionSampler;
 import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
 import com.android.systemui.statusbar.notification.AnimatableProperty;
 import com.android.systemui.statusbar.notification.PropertyAnimator;
@@ -445,6 +446,10 @@
         if (clock != null) {
             clock.dump(pw);
         }
+        final RegionSampler regionSampler = mClockEventController.getRegionSampler();
+        if (regionSampler != null) {
+            regionSampler.dump(pw);
+        }
     }
 
     /** Gets the animations for the current clock. */
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/ExtensionFragmentListener.java b/packages/SystemUI/src/com/android/systemui/fragments/ExtensionFragmentListener.java
index 18fb423..98896b1 100644
--- a/packages/SystemUI/src/com/android/systemui/fragments/ExtensionFragmentListener.java
+++ b/packages/SystemUI/src/com/android/systemui/fragments/ExtensionFragmentListener.java
@@ -37,9 +37,14 @@
     private final int mId;
     private String mOldClass;
 
-    private ExtensionFragmentListener(View view, String tag, int id, Extension<T> extension) {
+    private ExtensionFragmentListener(
+            FragmentService fragmentService,
+            View view,
+            String tag,
+            int id,
+            Extension<T> extension) {
         mTag = tag;
-        mFragmentHostManager = FragmentHostManager.get(view);
+        mFragmentHostManager = fragmentService.getFragmentHostManager(view);
         mExtension = extension;
         mId = id;
         mFragmentHostManager.getFragmentManager().beginTransaction()
@@ -61,8 +66,13 @@
         mExtension.clearItem(true);
     }
 
-    public static <T> void attachExtensonToFragment(View view, String tag, int id,
+    public static <T> void attachExtensonToFragment(
+            FragmentService fragmentService,
+            View view,
+            String tag,
+            int id,
             Extension<T> extension) {
-        extension.addCallback(new ExtensionFragmentListener(view, tag, id, extension));
+        extension.addCallback(
+                new ExtensionFragmentListener(fragmentService, view, tag, id, extension));
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
index 9c7411b..6a27ee7 100644
--- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
+++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
@@ -36,7 +36,6 @@
 import androidx.annotation.NonNull;
 
 import com.android.settingslib.applications.InterestingConfigChanges;
-import com.android.systemui.Dependency;
 import com.android.systemui.plugins.Plugin;
 import com.android.systemui.util.leak.LeakDetector;
 
@@ -46,12 +45,17 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+
 public class FragmentHostManager {
 
     private final Handler mHandler = new Handler(Looper.getMainLooper());
     private final Context mContext;
     private final HashMap<String, ArrayList<FragmentListener>> mListeners = new HashMap<>();
     private final View mRootView;
+    private final LeakDetector mLeakDetector;
     private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges(
             ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_LOCALE
                     | ActivityInfo.CONFIG_ASSETS_PATHS);
@@ -61,14 +65,24 @@
     private FragmentController mFragments;
     private FragmentLifecycleCallbacks mLifecycleCallbacks;
 
-    FragmentHostManager(FragmentService manager, View rootView) {
+    @AssistedInject
+    FragmentHostManager(
+            @Assisted View rootView,
+            FragmentService manager,
+            LeakDetector leakDetector) {
         mContext = rootView.getContext();
         mManager = manager;
         mRootView = rootView;
+        mLeakDetector = leakDetector;
         mConfigChanges.applyNewConfig(mContext.getResources());
         createFragmentHost(null);
     }
 
+    @AssistedFactory
+    public interface Factory {
+        FragmentHostManager create(View rootView);
+    }
+
     private void createFragmentHost(Parcelable savedState) {
         mFragments = FragmentController.createController(new HostCallbacks());
         mFragments.attachHost(null);
@@ -86,7 +100,7 @@
 
             @Override
             public void onFragmentDestroyed(FragmentManager fm, Fragment f) {
-                Dependency.get(LeakDetector.class).trackGarbage(f);
+                mLeakDetector.trackGarbage(f);
             }
         };
         mFragments.getFragmentManager().registerFragmentLifecycleCallbacks(mLifecycleCallbacks,
@@ -211,19 +225,6 @@
         }
     }
 
-    public static FragmentHostManager get(View view) {
-        try {
-            return Dependency.get(FragmentService.class).getFragmentHostManager(view);
-        } catch (ClassCastException e) {
-            // TODO: Some auto handling here?
-            throw e;
-        }
-    }
-
-    public static void removeAndDestroy(View view) {
-        Dependency.get(FragmentService.class).removeAndDestroy(view);
-    }
-
     public void reloadFragments() {
         Trace.beginSection("FrargmentHostManager#reloadFragments");
         // Save the old state.
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
index fe945fb..d302b13a 100644
--- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
+++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java
@@ -53,6 +53,7 @@
      */
     private final ArrayMap<String, FragmentInstantiationInfo> mInjectionMap = new ArrayMap<>();
     private final Handler mHandler = new Handler();
+    private final FragmentHostManager.Factory mFragmentHostManagerFactory;
 
     private ConfigurationController.ConfigurationListener mConfigurationListener =
             new ConfigurationController.ConfigurationListener() {
@@ -67,8 +68,10 @@
     @Inject
     public FragmentService(
             FragmentCreator.Factory fragmentCreatorFactory,
+            FragmentHostManager.Factory fragmentHostManagerFactory,
             ConfigurationController configurationController,
             DumpManager dumpManager) {
+        mFragmentHostManagerFactory = fragmentHostManagerFactory;
         addFragmentInstantiationProvider(fragmentCreatorFactory.build());
         configurationController.addCallback(mConfigurationListener);
 
@@ -152,7 +155,7 @@
 
         public FragmentHostState(View view) {
             mView = view;
-            mFragmentHostManager = new FragmentHostManager(FragmentService.this, mView);
+            mFragmentHostManager = mFragmentHostManagerFactory.create(mView);
         }
 
         public void sendConfigurationChange(Configuration newConfig) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index 51b5a3d..769e0c8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -16,17 +16,21 @@
 
 package com.android.systemui.media.dialog;
 
+import android.content.Context;
 import android.content.res.ColorStateList;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.CheckBox;
 import android.widget.TextView;
 
+import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
 import androidx.core.widget.CompoundButtonCompat;
 import androidx.recyclerview.widget.RecyclerView;
 
@@ -186,6 +190,17 @@
                     mCurrentActivePosition = position;
                     updateFullItemClickListener(v -> onItemClick(v, device));
                     setSingleLineLayout(getItemTitle(device));
+                } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
+                        && mController.isSubStatusSupported() && device.hasDisabledReason()) {
+                    //update to subtext with device status
+                    setUpDeviceIcon(device);
+                    mSubTitleText.setText(
+                            Api34Impl.composeDisabledReason(device.getDisableReason(), mContext));
+                    updateConnectionFailedStatusIcon();
+                    updateFullItemClickListener(null);
+                    setTwoLineLayout(device, false /* bFocused */, false /* showSeekBar */,
+                            false /* showProgressBar */, true /* showSubtitle */,
+                            true /* showStatus */);
                 } else if (device.getState() == MediaDeviceState.STATE_CONNECTING_FAILED) {
                     setUpDeviceIcon(device);
                     updateConnectionFailedStatusIcon();
@@ -389,4 +404,12 @@
             mTitleText.setText(groupDividerTitle);
         }
     }
+
+    @RequiresApi(34)
+    private static class Api34Impl {
+        @DoNotInline
+        static String composeDisabledReason(int reason, Context context) {
+            return "";
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index 4e08050..dc75538 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -45,6 +45,7 @@
 import android.widget.TextView;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
 import androidx.recyclerview.widget.RecyclerView;
 
 import com.android.settingslib.Utils;
@@ -142,11 +143,12 @@
         final TextView mVolumeValueText;
         final ImageView mTitleIcon;
         final ProgressBar mProgressBar;
-        final MediaOutputSeekbar mSeekBar;
         final LinearLayout mTwoLineLayout;
         final ImageView mStatusIcon;
         final CheckBox mCheckBox;
         final ViewGroup mEndTouchArea;
+        @VisibleForTesting
+        MediaOutputSeekbar mSeekBar;
         private String mDeviceId;
         private ValueAnimator mCornerAnimator;
         private ValueAnimator mVolumeAnimator;
@@ -390,6 +392,7 @@
                         mTitleIcon.setVisibility(View.VISIBLE);
                         mVolumeValueText.setVisibility(View.GONE);
                     }
+                    mController.logInteractionAdjustVolume(device);
                     mIsDragging = false;
                 }
             });
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index f95da27..5f5c686 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -141,7 +141,7 @@
     @VisibleForTesting
     LocalMediaManager mLocalMediaManager;
     @VisibleForTesting
-    private MediaOutputMetricLogger mMetricLogger;
+    MediaOutputMetricLogger mMetricLogger;
     private int mCurrentState;
 
     private int mColorItemContent;
@@ -757,6 +757,10 @@
         return mFeatureFlags.isEnabled(Flags.OUTPUT_SWITCHER_ROUTES_PROCESSING);
     }
 
+    public boolean isSubStatusSupported() {
+        return mFeatureFlags.isEnabled(Flags.OUTPUT_SWITCHER_DEVICE_STATUS);
+    }
+
     List<MediaDevice> getGroupMediaDevices() {
         final List<MediaDevice> selectedDevices = getSelectedMediaDevice();
         final List<MediaDevice> selectableDevices = getSelectableMediaDevice();
@@ -866,12 +870,15 @@
     }
 
     void adjustVolume(MediaDevice device, int volume) {
-        mMetricLogger.logInteractionAdjustVolume(device);
         ThreadUtils.postOnBackgroundThread(() -> {
             device.requestSetVolume(volume);
         });
     }
 
+    void logInteractionAdjustVolume(MediaDevice device) {
+        mMetricLogger.logInteractionAdjustVolume(device);
+    }
+
     String getPackageName() {
         return mPackageName;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt
new file mode 100644
index 0000000..1e531ba
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt
@@ -0,0 +1,113 @@
+package com.android.systemui.screenshot
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.ValueAnimator
+import android.graphics.drawable.Drawable
+import android.view.View
+import android.view.ViewGroup
+import android.view.ViewGroup.MarginLayoutParams
+import android.view.ViewTreeObserver
+import android.view.animation.AccelerateDecelerateInterpolator
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.constraintlayout.widget.Guideline
+import com.android.systemui.R
+
+/**
+ * MessageContainerController controls the display of content in the screenshot message container.
+ */
+class MessageContainerController
+constructor(
+    parent: ViewGroup,
+) {
+    private val guideline: Guideline = parent.requireViewById(R.id.guideline)
+    private val messageContainer: ViewGroup =
+        parent.requireViewById(R.id.screenshot_message_container)
+
+    /**
+     * Show a notification under the screenshot view indicating that a work profile screenshot has
+     * been taken and which app can be used to view it.
+     *
+     * @param appName The name of the app to use to view screenshots
+     * @param appIcon Optional icon for the relevant files app
+     * @param onDismiss Runnable to be run when the user dismisses this message
+     */
+    fun showWorkProfileMessage(appName: CharSequence, appIcon: Drawable?, onDismiss: Runnable) {
+        // Eventually this container will support multiple notification types, but for now just make
+        // sure we don't double inflate.
+        if (messageContainer.childCount == 0) {
+            View.inflate(
+                messageContainer.context,
+                R.layout.screenshot_work_profile_first_run,
+                messageContainer
+            )
+        }
+        if (appIcon != null) {
+            // Replace the default icon if one is provided.
+            val imageView: ImageView =
+                messageContainer.requireViewById<ImageView>(R.id.screenshot_message_icon)
+            imageView.setImageDrawable(appIcon)
+        }
+        val messageContent =
+            messageContainer.requireViewById<TextView>(R.id.screenshot_message_content)
+        messageContent.text =
+            messageContainer.context.getString(
+                R.string.screenshot_work_profile_notification,
+                appName
+            )
+        messageContainer.requireViewById<View>(R.id.message_dismiss_button).setOnClickListener {
+            animateOutMessageContainer()
+            onDismiss.run()
+        }
+
+        // Need the container to be fully measured before animating in (to know animation offset
+        // destination)
+        messageContainer.viewTreeObserver.addOnPreDrawListener(
+            object : ViewTreeObserver.OnPreDrawListener {
+                override fun onPreDraw(): Boolean {
+                    messageContainer.viewTreeObserver.removeOnPreDrawListener(this)
+                    animateInMessageContainer()
+                    return false
+                }
+            }
+        )
+    }
+
+    private fun animateInMessageContainer() {
+        if (messageContainer.visibility == View.VISIBLE) return
+
+        messageContainer.visibility = View.VISIBLE
+        getAnimator(true).start()
+    }
+
+    private fun animateOutMessageContainer() {
+        getAnimator(false).apply {
+            addListener(
+                object : AnimatorListenerAdapter() {
+                    override fun onAnimationEnd(animation: Animator) {
+                        super.onAnimationEnd(animation)
+                        messageContainer.visibility = View.INVISIBLE
+                    }
+                }
+            )
+            start()
+        }
+    }
+
+    private fun getAnimator(animateIn: Boolean): Animator {
+        val params = messageContainer.layoutParams as MarginLayoutParams
+        val offset = messageContainer.height + params.topMargin + params.bottomMargin
+        val anim = if (animateIn) ValueAnimator.ofFloat(0f, 1f) else ValueAnimator.ofFloat(1f, 0f)
+        with(anim) {
+            duration = ScreenshotView.SCREENSHOT_ACTIONS_EXPANSION_DURATION_MS
+            interpolator = AccelerateDecelerateInterpolator()
+            addUpdateListener { valueAnimator: ValueAnimator ->
+                val interpolation = valueAnimator.animatedValue as Float
+                guideline.setGuidelineEnd((interpolation * offset).toInt())
+                messageContainer.alpha = interpolation
+            }
+        }
+        return anim
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 9f7d4f0..ab13962 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -293,6 +293,7 @@
     };
 
     private ScreenshotView mScreenshotView;
+    private MessageContainerController mMessageContainerController;
     private Bitmap mScreenBitmap;
     private SaveImageInBackgroundTask mSaveInBgTask;
     private boolean mScreenshotTakenInPortrait;
@@ -631,6 +632,7 @@
         // Inflate the screenshot layout
         mScreenshotView = (ScreenshotView)
                 LayoutInflater.from(mContext).inflate(R.layout.screenshot, null);
+        mMessageContainerController = new MessageContainerController(mScreenshotView);
         mScreenshotView.addOnAttachStateChangeListener(
                 new View.OnAttachStateChangeListener() {
                     @Override
@@ -1193,7 +1195,8 @@
     private void doPostAnimation(ScreenshotController.SavedImageData imageData) {
         mScreenshotView.setChipIntents(imageData);
         if (mFlags.isEnabled(SCREENSHOT_WORK_PROFILE_POLICY)) {
-            mWorkProfileMessageController.onScreenshotTaken(imageData.owner, mScreenshotView);
+            mWorkProfileMessageController.onScreenshotTaken(imageData.owner,
+                    mMessageContainerController);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index 8b73a57f..afba7ad 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -33,7 +33,6 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
-import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.Notification;
 import android.app.PendingIntent;
@@ -82,7 +81,6 @@
 import android.widget.HorizontalScrollView;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
-import android.widget.TextView;
 
 import androidx.constraintlayout.widget.ConstraintLayout;
 
@@ -102,8 +100,7 @@
  * Handles the visual elements and animations for the screenshot flow.
  */
 public class ScreenshotView extends FrameLayout implements
-        ViewTreeObserver.OnComputeInternalInsetsListener,
-        WorkProfileMessageController.WorkProfileMessageDisplay {
+        ViewTreeObserver.OnComputeInternalInsetsListener {
 
     interface ScreenshotViewCallback {
         void onUserInteraction();
@@ -123,7 +120,7 @@
     private static final long SCREENSHOT_TO_CORNER_X_DURATION_MS = 234;
     private static final long SCREENSHOT_TO_CORNER_Y_DURATION_MS = 500;
     private static final long SCREENSHOT_TO_CORNER_SCALE_DURATION_MS = 234;
-    private static final long SCREENSHOT_ACTIONS_EXPANSION_DURATION_MS = 400;
+    public static final long SCREENSHOT_ACTIONS_EXPANSION_DURATION_MS = 400;
     private static final long SCREENSHOT_ACTIONS_ALPHA_DURATION_MS = 100;
     private static final float SCREENSHOT_ACTIONS_START_SCALE_X = .7f;
     private static final int SWIPE_PADDING_DP = 12; // extra padding around views to allow swipe
@@ -142,8 +139,6 @@
 
     private ImageView mScrollingScrim;
     private DraggableConstraintLayout mScreenshotStatic;
-    private ViewGroup mMessageContainer;
-    private TextView mMessageContent;
     private ImageView mScreenshotPreview;
     private ImageView mScreenshotBadge;
     private View mScreenshotPreviewBorder;
@@ -295,8 +290,11 @@
         mDismissButton.getBoundsOnScreen(tmpRect);
         swipeRegion.op(tmpRect, Region.Op.UNION);
 
-        mMessageContainer.findViewById(R.id.message_dismiss_button).getBoundsOnScreen(tmpRect);
-        swipeRegion.op(tmpRect, Region.Op.UNION);
+        View messageDismiss = findViewById(R.id.message_dismiss_button);
+        if (messageDismiss != null) {
+            messageDismiss.getBoundsOnScreen(tmpRect);
+            swipeRegion.op(tmpRect, Region.Op.UNION);
+        }
 
         return swipeRegion;
     }
@@ -352,39 +350,11 @@
         }
     }
 
-    /**
-     * Show a notification under the screenshot view indicating that a work profile screenshot has
-     * been taken and which app can be used to view it.
-     *
-     * @param appName The name of the app to use to view screenshots
-     * @param appIcon Optional icon for the relevant files app
-     * @param onDismiss Runnable to be run when the user dismisses this message
-     */
-    @Override
-    public void showWorkProfileMessage(CharSequence appName, @Nullable Drawable appIcon,
-            Runnable onDismiss) {
-        if (appIcon != null) {
-            // Replace the default icon if one is provided.
-            ImageView imageView = mMessageContainer.findViewById(R.id.screenshot_message_icon);
-            imageView.setImageDrawable(appIcon);
-        }
-        mMessageContent.setText(
-                mContext.getString(R.string.screenshot_work_profile_notification, appName));
-        mMessageContainer.setVisibility(VISIBLE);
-        mMessageContainer.findViewById(R.id.message_dismiss_button).setOnClickListener((v) -> {
-            mMessageContainer.setVisibility(View.GONE);
-            onDismiss.run();
-        });
-    }
-
     @Override // View
     protected void onFinishInflate() {
+        super.onFinishInflate();
         mScrollingScrim = requireNonNull(findViewById(R.id.screenshot_scrolling_scrim));
         mScreenshotStatic = requireNonNull(findViewById(R.id.screenshot_static));
-        mMessageContainer =
-                requireNonNull(mScreenshotStatic.findViewById(R.id.screenshot_message_container));
-        mMessageContent =
-                requireNonNull(mMessageContainer.findViewById(R.id.screenshot_message_content));
         mScreenshotPreview = requireNonNull(findViewById(R.id.screenshot_preview));
 
         mScreenshotPreviewBorder = requireNonNull(
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/WorkProfileMessageController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/WorkProfileMessageController.kt
index 5d7e56f..b4a07d4 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/WorkProfileMessageController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/WorkProfileMessageController.kt
@@ -40,10 +40,10 @@
 ) {
 
     /**
-     * Determine if a message should be shown to the user, send message details to messageDisplay if
-     * appropriate.
+     * Determine if a message should be shown to the user, send message details to
+     * MessageContainerController if appropriate.
      */
-    fun onScreenshotTaken(userHandle: UserHandle, messageDisplay: WorkProfileMessageDisplay) {
+    fun onScreenshotTaken(userHandle: UserHandle, messageContainer: MessageContainerController) {
         if (userManager.isManagedProfile(userHandle.identifier) && !messageAlreadyDismissed()) {
             var badgedIcon: Drawable? = null
             var label: CharSequence? = null
@@ -65,7 +65,9 @@
             val badgedLabel =
                 packageManager.getUserBadgedLabel(label ?: defaultFileAppName(), userHandle)
 
-            messageDisplay.showWorkProfileMessage(badgedLabel, badgedIcon) { onMessageDismissed() }
+            messageContainer.showWorkProfileMessage(badgedLabel, badgedIcon) {
+                onMessageDismissed()
+            }
         }
     }
 
@@ -89,15 +91,6 @@
 
     private fun defaultFileAppName() = context.getString(R.string.screenshot_default_files_app_name)
 
-    /** UI that can show work profile messages (ScreenshotView in practice) */
-    interface WorkProfileMessageDisplay {
-        /**
-         * Show the given message and icon, calling onDismiss if the user explicitly dismisses the
-         * message.
-         */
-        fun showWorkProfileMessage(text: CharSequence, icon: Drawable?, onDismiss: Runnable)
-    }
-
     companion object {
         const val TAG = "WorkProfileMessageCtrl"
         const val SHARED_PREFERENCES_NAME = "com.android.systemui.screenshot"
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
index 85b259e..de02115 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
@@ -31,6 +31,7 @@
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
+import com.android.systemui.fragments.FragmentService
 import com.android.systemui.navigationbar.NavigationModeController
 import com.android.systemui.plugins.qs.QS
 import com.android.systemui.plugins.qs.QSContainerController
@@ -54,6 +55,7 @@
     private val largeScreenShadeHeaderController: LargeScreenShadeHeaderController,
     private val shadeExpansionStateManager: ShadeExpansionStateManager,
     private val featureFlags: FeatureFlags,
+    private val fragmentService: FragmentService,
     @Main private val delayableExecutor: DelayableExecutor
 ) : ViewController<NotificationsQuickSettingsContainer>(view), QSContainerController {
 
@@ -128,6 +130,7 @@
         mView.setInsetsChangedListener(delayedInsetSetter)
         mView.setQSFragmentAttachedListener { qs: QS -> qs.setContainerController(this) }
         mView.setConfigurationChangedListener { updateResources() }
+        fragmentService.getFragmentHostManager(mView).addTagListener(QS.TAG, mView)
     }
 
     override fun onViewDetached() {
@@ -136,6 +139,7 @@
         mView.removeOnInsetsChangedListener()
         mView.removeQSFragmentAttachedListener()
         mView.setConfigurationChangedListener(null)
+        fragmentService.getFragmentHostManager(mView).removeTagListener(QS.TAG, mView)
     }
 
     fun updateResources() {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
index 02316b7..f73dde6 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
@@ -29,7 +29,6 @@
 import androidx.constraintlayout.widget.ConstraintSet;
 
 import com.android.systemui.R;
-import com.android.systemui.fragments.FragmentHostManager;
 import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.statusbar.notification.AboveShelfObserver;
@@ -133,18 +132,6 @@
     }
 
     @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        FragmentHostManager.get(this).addTagListener(QS.TAG, this);
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        FragmentHostManager.get(this).removeTagListener(QS.TAG, this);
-    }
-
-    @Override
     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
         mInsetsChangedListener.accept(insets);
         return insets;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 5ff755e..85399ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -1306,8 +1306,13 @@
         // Set up the quick settings tile panel
         final View container = mNotificationShadeWindowView.findViewById(R.id.qs_frame);
         if (container != null) {
-            FragmentHostManager fragmentHostManager = FragmentHostManager.get(container);
-            ExtensionFragmentListener.attachExtensonToFragment(container, QS.TAG, R.id.qs_frame,
+            FragmentHostManager fragmentHostManager =
+                    mFragmentService.getFragmentHostManager(container);
+            ExtensionFragmentListener.attachExtensonToFragment(
+                    mFragmentService,
+                    container,
+                    QS.TAG,
+                    R.id.qs_frame,
                     mExtensionController
                             .newExtension(QS.class)
                             .withPlugin(QS.class)
@@ -1478,7 +1483,9 @@
     }
 
     protected QS createDefaultQSFragment() {
-        return FragmentHostManager.get(mNotificationShadeWindowView).create(QSFragment.class);
+        return mFragmentService
+                .getFragmentHostManager(mNotificationShadeWindowView)
+                .create(QSFragment.class);
     }
 
     private void setUpPresenter() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
index e0d780a..7a4e35f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
@@ -48,6 +48,7 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.fragments.FragmentHostManager;
+import com.android.systemui.fragments.FragmentService;
 import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider;
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
 import com.android.systemui.unfold.util.JankMonitorTransitionProgressListener;
@@ -73,6 +74,7 @@
     private boolean mIsAttached;
 
     private final ViewGroup mStatusBarWindowView;
+    private final FragmentService mFragmentService;
     // The container in which we should run launch animations started from the status bar and
     //   expanding into the opening window.
     private final ViewGroup mLaunchAnimationContainer;
@@ -86,6 +88,7 @@
             WindowManager windowManager,
             IWindowManager iWindowManager,
             StatusBarContentInsetsProvider contentInsetsProvider,
+            FragmentService fragmentService,
             @Main Resources resources,
             Optional<UnfoldTransitionProgressProvider> unfoldTransitionProgressProvider) {
         mContext = context;
@@ -93,6 +96,7 @@
         mIWindowManager = iWindowManager;
         mContentInsetsProvider = contentInsetsProvider;
         mStatusBarWindowView = statusBarWindowView;
+        mFragmentService = fragmentService;
         mLaunchAnimationContainer = mStatusBarWindowView.findViewById(
                 R.id.status_bar_launch_animation_container);
         mLpChanged = new WindowManager.LayoutParams();
@@ -157,7 +161,7 @@
 
     /** Returns a fragment host manager for the status bar window view. */
     public FragmentHostManager getFragmentHostManager() {
-        return FragmentHostManager.get(mStatusBarWindowView);
+        return mFragmentService.getFragmentHostManager(mStatusBarWindowView);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/RadioListPreference.java b/packages/SystemUI/src/com/android/systemui/tuner/RadioListPreference.java
index 79811c5..2475890 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/RadioListPreference.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/RadioListPreference.java
@@ -28,8 +28,9 @@
 import androidx.preference.PreferenceScreen;
 
 import com.android.settingslib.Utils;
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
-import com.android.systemui.fragments.FragmentHostManager;
+import com.android.systemui.fragments.FragmentService;
 
 import java.util.Objects;
 
@@ -74,7 +75,7 @@
 
         RadioFragment f = new RadioFragment();
         f.setPreference(this);
-        FragmentHostManager.get(v).getFragmentManager()
+        Dependency.get(FragmentService.class).getFragmentHostManager(v).getFragmentManager()
                 .beginTransaction()
                 .add(android.R.id.content, f)
                 .commit();
@@ -86,8 +87,10 @@
             Bundle savedInstanceState) {
         super.onDialogStateRestored(fragment, dialog, savedInstanceState);
         View view = dialog.findViewById(R.id.content);
-        RadioFragment radioFragment = (RadioFragment) FragmentHostManager.get(view)
-                .getFragmentManager().findFragmentById(R.id.content);
+        RadioFragment radioFragment = (RadioFragment) Dependency.get(FragmentService.class)
+                .getFragmentHostManager(view)
+                .getFragmentManager()
+                .findFragmentById(R.id.content);
         if (radioFragment != null) {
             radioFragment.setPreference(this);
         }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index 00b2fbe..b9c23d4 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -15,7 +15,6 @@
  */
 package com.android.keyguard
 
-import com.android.systemui.statusbar.CommandQueue
 import android.content.BroadcastReceiver
 import android.testing.AndroidTestingRunner
 import android.view.View
@@ -34,6 +33,7 @@
 import com.android.systemui.plugins.ClockFaceController
 import com.android.systemui.plugins.ClockFaceEvents
 import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.statusbar.policy.BatteryController
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.mockito.any
@@ -41,8 +41,6 @@
 import com.android.systemui.util.mockito.capture
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.mock
-import java.util.TimeZone
-import java.util.concurrent.Executor
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.yield
@@ -57,8 +55,10 @@
 import org.mockito.Mockito.never
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
 import org.mockito.junit.MockitoJUnit
+import java.util.*
+import java.util.concurrent.Executor
+import org.mockito.Mockito.`when` as whenever
 
 @RunWith(AndroidTestingRunner::class)
 @SmallTest
diff --git a/packages/SystemUI/tests/src/com/android/systemui/fragments/FragmentServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/fragments/FragmentServiceTest.kt
index 77c837b..a2dc1eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/fragments/FragmentServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/fragments/FragmentServiceTest.kt
@@ -14,6 +14,7 @@
 @SmallTest
 class FragmentServiceTest : SysuiTestCase() {
     private val fragmentCreator = TestFragmentCreator()
+    private val fragmenetHostManagerFactory: FragmentHostManager.Factory = mock()
     private val fragmentCreatorFactory = FragmentService.FragmentCreator.Factory { fragmentCreator }
 
     private lateinit var fragmentService: FragmentService
@@ -24,7 +25,13 @@
             Looper.prepare()
         }
 
-        fragmentService = FragmentService(fragmentCreatorFactory, mock(), DumpManager())
+        fragmentService =
+            FragmentService(
+                fragmentCreatorFactory,
+                fragmenetHostManagerFactory,
+                mock(),
+                DumpManager()
+            )
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 7c3c9d2..8fd15c1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -29,6 +29,7 @@
 import android.testing.AndroidTestingRunner;
 import android.view.View;
 import android.widget.LinearLayout;
+import android.widget.SeekBar;
 
 import androidx.core.graphics.drawable.IconCompat;
 import androidx.test.filters.SmallTest;
@@ -43,6 +44,8 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -57,6 +60,7 @@
     private static final String TEST_DEVICE_ID_1 = "test_device_id_1";
     private static final String TEST_DEVICE_ID_2 = "test_device_id_2";
     private static final String TEST_SESSION_NAME = "test_session_name";
+
     private static final int TEST_MAX_VOLUME = 20;
     private static final int TEST_CURRENT_VOLUME = 10;
 
@@ -69,6 +73,8 @@
     private IconCompat mIconCompat = mock(IconCompat.class);
     private View mDialogLaunchView = mock(View.class);
 
+    @Captor
+    private ArgumentCaptor<SeekBar.OnSeekBarChangeListener> mOnSeekBarChangeListenerCaptor;
     private MediaOutputAdapter mMediaOutputAdapter;
     private MediaOutputAdapter.MediaDeviceViewHolder mViewHolder;
     private List<MediaDevice> mMediaDevices = new ArrayList<>();
@@ -78,6 +84,7 @@
     @Before
     public void setUp() {
         when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(false);
+        when(mMediaOutputController.isSubStatusSupported()).thenReturn(false);
         when(mMediaOutputController.getMediaItemList()).thenReturn(mMediaItems);
         when(mMediaOutputController.getMediaDevices()).thenReturn(mMediaDevices);
         when(mMediaOutputController.hasAdjustVolumeUserRestriction()).thenReturn(false);
@@ -348,6 +355,24 @@
     }
 
     @Test
+    public void onBindViewHolder_dragSeekbar_setsVolume() {
+        mOnSeekBarChangeListenerCaptor = ArgumentCaptor.forClass(
+                SeekBar.OnSeekBarChangeListener.class);
+        MediaOutputSeekbar mSpySeekbar = spy(mViewHolder.mSeekBar);
+        mViewHolder.mSeekBar = mSpySeekbar;
+        when(mMediaDevice1.getMaxVolume()).thenReturn(TEST_MAX_VOLUME);
+        when(mMediaDevice1.getCurrentVolume()).thenReturn(TEST_MAX_VOLUME);
+        mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
+
+        verify(mViewHolder.mSeekBar).setOnSeekBarChangeListener(
+                mOnSeekBarChangeListenerCaptor.capture());
+
+        mOnSeekBarChangeListenerCaptor.getValue().onStopTrackingTouch(mViewHolder.mSeekBar);
+        assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
+        verify(mMediaOutputController).logInteractionAdjustVolume(mMediaDevice1);
+    }
+
+    @Test
     public void onBindViewHolder_bindSelectableDevice_verifyView() {
         List<MediaDevice> selectableDevices = new ArrayList<>();
         selectableDevices.add(mMediaDevice2);
@@ -404,6 +429,24 @@
     }
 
     @Test
+    public void subStatusSupported_onBindViewHolder_bindFailedStateDevice_verifyView() {
+        String deviceStatus = "";
+        when(mMediaOutputController.isSubStatusSupported()).thenReturn(true);
+        when(mMediaDevice2.hasDisabledReason()).thenReturn(true);
+        when(mMediaDevice2.getDisableReason()).thenReturn(-1);
+        mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
+
+        assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mSubTitleText.getText()).isEqualTo(deviceStatus);
+        assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_2);
+    }
+
+    @Test
     public void onBindViewHolder_inTransferring_bindTransferringDevice_verifyView() {
         when(mMediaOutputController.isAnyDeviceTransferring()).thenReturn(true);
         when(mMediaDevice1.getState()).thenReturn(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index 117751c..7c36e46 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -93,6 +93,11 @@
     private static final String TEST_SONG = "test_song";
     private static final String TEST_SESSION_ID = "test_session_id";
     private static final String TEST_SESSION_NAME = "test_session_name";
+    private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
+    private final ActivityLaunchAnimator.Controller mActivityLaunchAnimatorController = mock(
+            ActivityLaunchAnimator.Controller.class);
+    private final NearbyMediaDevicesManager mNearbyMediaDevicesManager = mock(
+            NearbyMediaDevicesManager.class);
     // Mock
     private MediaController mMediaController = mock(MediaController.class);
     private MediaSessionManager mMediaSessionManager = mock(MediaSessionManager.class);
@@ -111,12 +116,7 @@
     private KeyguardManager mKeyguardManager = mock(KeyguardManager.class);
     private PowerExemptionManager mPowerExemptionManager = mock(PowerExemptionManager.class);
     private CommonNotifCollection mNotifCollection = mock(CommonNotifCollection.class);
-    private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
     private FeatureFlags mFlags = mock(FeatureFlags.class);
-    private final ActivityLaunchAnimator.Controller mActivityLaunchAnimatorController = mock(
-            ActivityLaunchAnimator.Controller.class);
-    private final NearbyMediaDevicesManager mNearbyMediaDevicesManager = mock(
-            NearbyMediaDevicesManager.class);
     private View mDialogLaunchView = mock(View.class);
     private MediaOutputController.Callback mCallback = mock(MediaOutputController.Callback.class);
 
@@ -522,6 +522,17 @@
     }
 
     @Test
+    public void logInteractionAdjustVolume_triggersFromMetricLogger() {
+        MediaOutputMetricLogger spyMediaOutputMetricLogger = spy(
+                mMediaOutputController.mMetricLogger);
+        mMediaOutputController.mMetricLogger = spyMediaOutputMetricLogger;
+
+        mMediaOutputController.logInteractionAdjustVolume(mMediaDevice1);
+
+        verify(spyMediaOutputMetricLogger).logInteractionAdjustVolume(mMediaDevice1);
+    }
+
+    @Test
     public void getSessionVolumeMax_triggersFromLocalMediaManager() {
         mMediaOutputController.getSessionVolumeMax();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/WorkProfileMessageControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/WorkProfileMessageControllerTest.java
index bd04b3c..e8905ab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/WorkProfileMessageControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/WorkProfileMessageControllerTest.java
@@ -65,7 +65,7 @@
     @Mock
     private Context mContext;
     @Mock
-    private WorkProfileMessageController.WorkProfileMessageDisplay mMessageDisplay;
+    private MessageContainerController mMessageDisplay;
     @Mock
     private Drawable mActivityIcon;
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationQSContainerControllerTest.kt
index bdafc7d..c915502a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationQSContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationQSContainerControllerTest.kt
@@ -13,8 +13,11 @@
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.fragments.FragmentHostManager
+import com.android.systemui.fragments.FragmentService
 import com.android.systemui.navigationbar.NavigationModeController
 import com.android.systemui.navigationbar.NavigationModeController.ModeChangedListener
+import com.android.systemui.plugins.qs.QS
 import com.android.systemui.recents.OverviewProxyService
 import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
 import com.android.systemui.util.concurrency.FakeExecutor
@@ -29,6 +32,7 @@
 import org.mockito.Mock
 import org.mockito.Mockito
 import org.mockito.Mockito.RETURNS_DEEP_STUBS
+import org.mockito.Mockito.any
 import org.mockito.Mockito.anyInt
 import org.mockito.Mockito.doNothing
 import org.mockito.Mockito.eq
@@ -69,6 +73,10 @@
     private lateinit var shadeExpansionStateManager: ShadeExpansionStateManager
     @Mock
     private lateinit var featureFlags: FeatureFlags
+    @Mock
+    private lateinit var fragmentService: FragmentService
+    @Mock
+    private lateinit var fragmentHostManager: FragmentHostManager
     @Captor
     lateinit var navigationModeCaptor: ArgumentCaptor<ModeChangedListener>
     @Captor
@@ -77,6 +85,8 @@
     lateinit var windowInsetsCallbackCaptor: ArgumentCaptor<Consumer<WindowInsets>>
     @Captor
     lateinit var constraintSetCaptor: ArgumentCaptor<ConstraintSet>
+    @Captor
+    lateinit var attachStateListenerCaptor: ArgumentCaptor<View.OnAttachStateChangeListener>
 
     private lateinit var controller: NotificationsQSContainerController
     private lateinit var navigationModeCallback: ModeChangedListener
@@ -91,8 +101,10 @@
         mContext.ensureTestableResources()
         whenever(notificationsQSContainer.context).thenReturn(mContext)
         whenever(notificationsQSContainer.resources).thenReturn(mContext.resources)
+        whenever(fragmentService.getFragmentHostManager(any())).thenReturn(fragmentHostManager)
         fakeSystemClock = FakeSystemClock()
         delayableExecutor = FakeExecutor(fakeSystemClock)
+
         controller = NotificationsQSContainerController(
                 notificationsQSContainer,
                 navigationModeController,
@@ -100,6 +112,7 @@
                 largeScreenShadeHeaderController,
                 shadeExpansionStateManager,
                 featureFlags,
+                fragmentService,
                 delayableExecutor
         )
 
@@ -114,9 +127,10 @@
         doNothing().`when`(notificationsQSContainer)
                 .setInsetsChangedListener(windowInsetsCallbackCaptor.capture())
         doNothing().`when`(notificationsQSContainer).applyConstraints(constraintSetCaptor.capture())
-
+        doNothing().`when`(notificationsQSContainer)
+            .addOnAttachStateChangeListener(attachStateListenerCaptor.capture())
         controller.init()
-        controller.onViewAttached()
+        attachStateListenerCaptor.value.onViewAttachedToWindow(notificationsQSContainer)
 
         navigationModeCallback = navigationModeCaptor.value
         taskbarVisibilityCallback = taskbarVisibilityCaptor.value
@@ -385,6 +399,7 @@
                 largeScreenShadeHeaderController,
                 shadeExpansionStateManager,
                 featureFlags,
+                fragmentService,
                 delayableExecutor
         )
         controller.updateConstraints()
@@ -426,6 +441,17 @@
         verify(largeScreenShadeHeaderController).startCustomizingAnimation(false, 100L)
     }
 
+    @Test
+    fun testTagListenerAdded() {
+        verify(fragmentHostManager).addTagListener(eq(QS.TAG), eq(notificationsQSContainer))
+    }
+
+    @Test
+    fun testTagListenerRemoved() {
+        attachStateListenerCaptor.value.onViewDetachedFromWindow(notificationsQSContainer)
+        verify(fragmentHostManager).removeTagListener(eq(QS.TAG), eq(notificationsQSContainer))
+    }
+
     private fun disableSplitShade() {
         setSplitShadeEnabled(false)
     }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 41721ce..2dad910 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -439,6 +439,9 @@
     // finished destroying itself.
     private static final int DESTROY_TIMEOUT = 10 * 1000;
 
+    // Rounding tolerance to be used in aspect ratio computations
+    private static final float ASPECT_RATIO_ROUNDING_TOLERANCE = 0.005f;
+
     final ActivityTaskManagerService mAtmService;
     @NonNull
     final ActivityInfo info; // activity info provided by developer in AndroidManifest
@@ -8950,7 +8953,7 @@
         int activityWidth = containingAppWidth;
         int activityHeight = containingAppHeight;
 
-        if (containingRatio > desiredAspectRatio) {
+        if (containingRatio - desiredAspectRatio > ASPECT_RATIO_ROUNDING_TOLERANCE) {
             if (containingAppWidth < containingAppHeight) {
                 // Width is the shorter side, so we use that to figure-out what the max. height
                 // should be given the aspect ratio.
@@ -8960,7 +8963,7 @@
                 // should be given the aspect ratio.
                 activityWidth = (int) ((activityHeight * desiredAspectRatio) + 0.5f);
             }
-        } else if (containingRatio < desiredAspectRatio) {
+        } else if (desiredAspectRatio - containingRatio > ASPECT_RATIO_ROUNDING_TOLERANCE) {
             boolean adjustWidth;
             switch (getRequestedConfigurationOrientation()) {
                 case ORIENTATION_LANDSCAPE:
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index f8fd2b9..cf7d5d9 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -2007,7 +2007,8 @@
                 dc.getDisplayPolicy().simulateLayoutDisplay(df);
                 final InsetsState insetsState = df.mInsetsState;
                 final Rect displayFrame = insetsState.getDisplayFrame();
-                final Insets decor = calculateDecorInsetsWithInternalTypes(insetsState);
+                final Insets decor = insetsState.calculateInsets(displayFrame, DECOR_TYPES,
+                        true /* ignoreVisibility */);
                 final Insets statusBar = insetsState.calculateInsets(displayFrame,
                         Type.statusBars(), true /* ignoreVisibility */);
                 mNonDecorInsets.set(decor.left, decor.top, decor.right, decor.bottom);
@@ -2039,17 +2040,8 @@
             }
         }
 
-        // TODO (b/235842600): Use public type once we can treat task bar as navigation bar.
-        static final int[] INTERNAL_DECOR_TYPES;
-        static {
-            final ArraySet<Integer> decorTypes = InsetsState.toInternalType(
-                    Type.displayCutout() | Type.navigationBars());
-            decorTypes.remove(ITYPE_EXTRA_NAVIGATION_BAR);
-            INTERNAL_DECOR_TYPES = new int[decorTypes.size()];
-            for (int i = 0; i < INTERNAL_DECOR_TYPES.length; i++) {
-                INTERNAL_DECOR_TYPES[i] = decorTypes.valueAt(i);
-            }
-        }
+
+        static final int DECOR_TYPES = Type.displayCutout() | Type.navigationBars();
 
         private final DisplayContent mDisplayContent;
         private final Info[] mInfoForRotation = new Info[4];
@@ -2076,20 +2068,6 @@
                 info.mNeedUpdate = true;
             }
         }
-
-        // TODO (b/235842600): Remove this method once we can treat task bar as navigation bar.
-        private static Insets calculateDecorInsetsWithInternalTypes(InsetsState state) {
-            final Rect frame = state.getDisplayFrame();
-            Insets insets = Insets.NONE;
-            for (int i = INTERNAL_DECOR_TYPES.length - 1; i >= 0; i--) {
-                final InsetsSource source = state.peekSource(INTERNAL_DECOR_TYPES[i]);
-                if (source != null) {
-                    insets = Insets.max(source.calculateInsets(frame, true /* ignoreVisibility */),
-                            insets);
-                }
-            }
-            return insets;
-        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 6f7ff5c..3404279 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -249,11 +249,13 @@
         mDeskDockRotation = readRotation(R.integer.config_deskDockRotation);
         mUndockedHdmiRotation = readRotation(R.integer.config_undockedHdmiRotation);
 
-        mRotation = readDefaultDisplayRotation(displayAddress);
+        int defaultRotation = readDefaultDisplayRotation(displayAddress);
+        mRotation = defaultRotation;
 
         if (isDefaultDisplay) {
             final Handler uiHandler = UiThread.getHandler();
-            mOrientationListener = new OrientationListener(mContext, uiHandler);
+            mOrientationListener =
+                    new OrientationListener(mContext, uiHandler, defaultRotation);
             mOrientationListener.setCurrentRotation(mRotation);
             mSettingsObserver = new SettingsObserver(uiHandler);
             mSettingsObserver.observe();
@@ -1735,8 +1737,9 @@
     private class OrientationListener extends WindowOrientationListener implements Runnable {
         transient boolean mEnabled;
 
-        OrientationListener(Context context, Handler handler) {
-            super(context, handler);
+        OrientationListener(Context context, Handler handler,
+                @Surface.Rotation int defaultRotation) {
+            super(context, handler, defaultRotation);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/wm/WindowOrientationListener.java b/services/core/java/com/android/server/wm/WindowOrientationListener.java
index 3e165e4..14c816d 100644
--- a/services/core/java/com/android/server/wm/WindowOrientationListener.java
+++ b/services/core/java/com/android/server/wm/WindowOrientationListener.java
@@ -88,14 +88,19 @@
 
     private final Object mLock = new Object();
 
+    @Surface.Rotation
+    private final int mDefaultRotation;
+
     /**
      * Creates a new WindowOrientationListener.
      *
      * @param context for the WindowOrientationListener.
      * @param handler Provides the Looper for receiving sensor updates.
+     * @param defaultRotation Default rotation of the display.
      */
-    public WindowOrientationListener(Context context, Handler handler) {
-        this(context, handler, SensorManager.SENSOR_DELAY_UI);
+    public WindowOrientationListener(Context context, Handler handler,
+            @Surface.Rotation int defaultRotation) {
+        this(context, handler, defaultRotation, SensorManager.SENSOR_DELAY_UI);
     }
 
     /**
@@ -103,7 +108,7 @@
      *
      * @param context for the WindowOrientationListener.
      * @param handler Provides the Looper for receiving sensor updates.
-     * @param wmService WindowManagerService to read the device config from.
+     * @param defaultRotation Default rotation of the display.
      * @param rate at which sensor events are processed (see also
      * {@link android.hardware.SensorManager SensorManager}). Use the default
      * value of {@link android.hardware.SensorManager#SENSOR_DELAY_NORMAL
@@ -111,10 +116,11 @@
      *
      * This constructor is private since no one uses it.
      */
-    private WindowOrientationListener(
-            Context context, Handler handler, int rate) {
+    private WindowOrientationListener(Context context, Handler handler,
+            @Surface.Rotation int defaultRotation, int rate) {
         mContext = context;
         mHandler = handler;
+        mDefaultRotation = defaultRotation;
         mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
         mRate = rate;
         List<Sensor> l = mSensorManager.getSensorList(Sensor.TYPE_DEVICE_ORIENTATION);
@@ -1159,7 +1165,7 @@
                                 "Reusing the last rotation resolution: " + mLastRotationResolution);
                         finalizeRotation(mLastRotationResolution);
                     } else {
-                        finalizeRotation(Surface.ROTATION_0);
+                        finalizeRotation(mDefaultRotation);
                     }
                     return;
                 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java
index 3de65c1..1b3a199 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java
@@ -132,6 +132,19 @@
         assertThat(mFinalizedRotation).isEqualTo(DEFAULT_SENSOR_ROTATION);
     }
 
+    @Test
+    public void testOnSensorChanged_screenLocked_doNotCallRotationResolverReturnDefaultRotation() {
+        mWindowOrientationListener = new TestableWindowOrientationListener(mMockContext,
+                mHandler, /* defaultRotation */ Surface.ROTATION_180);
+        mWindowOrientationListener.mRotationResolverService = mFakeRotationResolverInternal;
+        mWindowOrientationListener.mIsScreenLocked = true;
+
+        mWindowOrientationListener.mOrientationJudge.onSensorChanged(mFakeSensorEvent);
+
+        assertThat(mWindowOrientationListener.mIsOnProposedRotationChangedCalled).isFalse();
+        assertThat(mFinalizedRotation).isEqualTo(Surface.ROTATION_180);
+    }
+
     static final class TestableRotationResolver extends RotationResolverInternal {
         @Surface.Rotation
         RotationResolverCallbackInternal mCallback;
@@ -166,21 +179,17 @@
         }
     }
 
-    @Test
-    public void testOnSensorChanged_inLockScreen_doNotCallRotationResolver() {
-        mWindowOrientationListener.mIsScreenLocked = true;
-
-        mWindowOrientationListener.mOrientationJudge.onSensorChanged(mFakeSensorEvent);
-
-        assertThat(mWindowOrientationListener.mIsOnProposedRotationChangedCalled).isFalse();
-    }
-
     final class TestableWindowOrientationListener extends WindowOrientationListener {
         private boolean mIsOnProposedRotationChangedCalled = false;
         private boolean mIsScreenLocked;
 
         TestableWindowOrientationListener(Context context, Handler handler) {
-            super(context, handler);
+            this(context, handler, Surface.ROTATION_0);
+        }
+
+        TestableWindowOrientationListener(Context context, Handler handler,
+                @Surface.Rotation int defaultRotation) {
+            super(context, handler, defaultRotation);
             this.mOrientationJudge = new OrientationSensorJudge();
         }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index a09bb33..c9ff150 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -3055,6 +3055,20 @@
     }
 
     @Test
+    public void testApplyAspectRatio_containingRatioAlmostEqualToMaxRatio_boundsUnchanged() {
+        setUpDisplaySizeWithApp(1981, 2576);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mWm.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(0.5f);
+
+        final Rect originalBounds = new Rect(mActivity.getBounds());
+        prepareUnresizable(mActivity, 1.3f, SCREEN_ORIENTATION_UNSPECIFIED);
+
+        // The containing aspect ratio is now 1.3003534, while the desired aspect ratio is 1.3. The
+        // bounds of the activity should not be changed as the difference is too small
+        assertEquals(mActivity.getBounds(), originalBounds);
+    }
+
+    @Test
     public void testUpdateResolvedBoundsHorizontalPosition_activityFillParentWidth() {
         // When activity width equals parent width, multiplier shouldn't have any effect.
         assertHorizontalPositionForDifferentDisplayConfigsForLandscapeActivity(