Merge "Import translations. DO NOT MERGE ANYWHERE" into main
diff --git a/apct-tests/perftests/packagemanager/AndroidTest.xml b/apct-tests/perftests/packagemanager/AndroidTest.xml
index c9d45a6..db938e4 100644
--- a/apct-tests/perftests/packagemanager/AndroidTest.xml
+++ b/apct-tests/perftests/packagemanager/AndroidTest.xml
@@ -76,11 +76,6 @@
         <option name="test-file-name" value="QueriesAll49.apk"/>
     </target_preparer>
 
-    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
-        <option name="package" value="com.android.perftests.packagemanager"/>
-        <option name="hidden-api-checks" value="false"/>
-    </test>
-
     <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
         <option name="directory-keys" value="/data/local/PackageManagerPerfTests"/>
         <option name="collect-on-run-ended-only" value="true"/>
diff --git a/core/api/current.txt b/core/api/current.txt
index c189a24c..c86e4cd 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -282,7 +282,7 @@
     field public static final String REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS";
     field public static final String REQUEST_INSTALL_PACKAGES = "android.permission.REQUEST_INSTALL_PACKAGES";
     field public static final String REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE = "android.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE";
-    field @FlaggedApi("android.companion.flags.device_presence") public static final String REQUEST_OBSERVE_DEVICE_UUID_PRESENCE = "android.permission.REQUEST_OBSERVE_DEVICE_UUID_PRESENCE";
+    field @FlaggedApi("android.companion.device_presence") public static final String REQUEST_OBSERVE_DEVICE_UUID_PRESENCE = "android.permission.REQUEST_OBSERVE_DEVICE_UUID_PRESENCE";
     field public static final String REQUEST_PASSWORD_COMPLEXITY = "android.permission.REQUEST_PASSWORD_COMPLEXITY";
     field @Deprecated public static final String RESTART_PACKAGES = "android.permission.RESTART_PACKAGES";
     field public static final String RUN_USER_INITIATED_JOBS = "android.permission.RUN_USER_INITIATED_JOBS";
@@ -39388,10 +39388,8 @@
   }
 
   public final class FileIntegrityManager {
-    method @FlaggedApi("android.security.fsverity_api") @Nullable public byte[] getFsVerityDigest(@NonNull java.io.File) throws java.io.IOException;
     method public boolean isApkVeritySupported();
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES, android.Manifest.permission.REQUEST_INSTALL_PACKAGES}) public boolean isAppSourceCertificateTrusted(@NonNull java.security.cert.X509Certificate) throws java.security.cert.CertificateEncodingException;
-    method @FlaggedApi("android.security.fsverity_api") public void setupFsVerity(@NonNull java.io.File) throws java.io.IOException;
   }
 
   public final class KeyChain {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index b767c52..bed8c41 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -355,6 +355,7 @@
     field public static final String SEND_SHOW_SUSPENDED_APP_DETAILS = "android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS";
     field public static final String SEND_SMS_NO_CONFIRMATION = "android.permission.SEND_SMS_NO_CONFIRMATION";
     field public static final String SERIAL_PORT = "android.permission.SERIAL_PORT";
+    field @FlaggedApi("android.security.fsverity_api") public static final String SETUP_FSVERITY = "android.permission.SETUP_FSVERITY";
     field public static final String SET_ACTIVITY_WATCHER = "android.permission.SET_ACTIVITY_WATCHER";
     field public static final String SET_CLIP_SOURCE = "android.permission.SET_CLIP_SOURCE";
     field public static final String SET_DEFAULT_ACCOUNT_FOR_CONTACTS = "android.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS";
@@ -1144,6 +1145,7 @@
   public static final class StatusBarManager.DisableInfo implements android.os.Parcelable {
     method public boolean areAllComponentsEnabled();
     method public int describeContents();
+    method public boolean isBackDisabled();
     method public boolean isNavigateToHomeDisabled();
     method public boolean isNotificationPeekingDisabled();
     method public boolean isRecentsDisabled();
@@ -12106,6 +12108,11 @@
 
 package android.security {
 
+  public final class FileIntegrityManager {
+    method @FlaggedApi("android.security.fsverity_api") @Nullable public byte[] getFsVerityDigest(@NonNull java.io.File) throws java.io.IOException;
+    method @FlaggedApi("android.security.fsverity_api") public void setupFsVerity(@NonNull java.io.File) throws java.io.IOException;
+  }
+
   public final class KeyChain {
     method @Nullable @WorkerThread public static String getWifiKeyGrantAsUser(@NonNull android.content.Context, @NonNull android.os.UserHandle, @NonNull String);
     method @WorkerThread public static boolean hasWifiKeyGrantAsUser(@NonNull android.content.Context, @NonNull android.os.UserHandle, @NonNull String);
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 301fef8..e6e46dd 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -1448,6 +1448,7 @@
          *
          * @hide
          */
+        @SystemApi
         public boolean isBackDisabled() {
             return mBack;
         }
@@ -1861,38 +1862,38 @@
         };
 
         @DataClass.Generated(
-                time = 1708625947132L,
+                time = 1707345957771L,
                 codegenVersion = "1.0.23",
                 sourceFile = "frameworks/base/core/java/android/app/StatusBarManager.java",
-                inputSignatures = "private  boolean mStatusBarExpansion\nprivate  boolean "
-                        + "mNavigateHome\nprivate  boolean mNotificationPeeking\nprivate  "
-                        + "boolean mRecents\nprivate  boolean mBack\nprivate  boolean mSearch\n"
-                        + "private  boolean mSystemIcons\nprivate  boolean mClock\nprivate  "
-                        + "boolean mNotificationIcons\nprivate  boolean mRotationSuggestion\n"
+                inputSignatures = "private  boolean mStatusBarExpansion\nprivate  "
+                        + "boolean mNavigateHome\nprivate  boolean mNotificationPeeking\nprivate  "
+                        + "boolean mRecents\nprivate  boolean mBack\nprivate  boolean "
+                        + "mSearch\nprivate  boolean mSystemIcons\nprivate  boolean mClock\nprivate"
+                        + "  boolean mNotificationIcons\nprivate  boolean mRotationSuggestion\n"
                         + "private  boolean mNotificationTicker\npublic "
                         + "@android.annotation.SystemApi boolean isStatusBarExpansionDisabled()\n"
                         + "public  void setStatusBarExpansionDisabled(boolean)\npublic "
-                        + "@android.annotation.SystemApi boolean isNavigateToHomeDisabled()\npublic"
-                        + "  void setNavigationHomeDisabled(boolean)\npublic "
-                        + "@android.annotation.SystemApi boolean isNotificationPeekingDisabled()"
-                        + "\npublic  void setNotificationPeekingDisabled(boolean)\npublic "
+                        + "@android.annotation.SystemApi boolean isNavigateToHomeDisabled()\n"
+                        + "public  void setNavigationHomeDisabled(boolean)\npublic "
+                        + "@android.annotation.SystemApi boolean isNotificationPeekingDisabled()\n"
+                        + "public  void setNotificationPeekingDisabled(boolean)\npublic "
                         + "@android.annotation.SystemApi boolean isRecentsDisabled()\npublic  "
-                        + "void setRecentsDisabled(boolean)\npublic  boolean isBackDisabled()"
-                        + "\npublic  void setBackDisabled(boolean)\npublic "
+                        + "void setRecentsDisabled(boolean)\npublic @android.annotation.SystemApi "
+                        + "boolean isBackDisabled()\npublic  void setBackDisabled(boolean)\npublic "
                         + "@android.annotation.SystemApi boolean isSearchDisabled()\npublic  "
                         + "void setSearchDisabled(boolean)\npublic  boolean "
-                        + "areSystemIconsDisabled()\npublic  void setSystemIconsDisabled(boolean)\n"
-                        + "public  boolean isClockDisabled()\npublic  "
-                        + "void setClockDisabled(boolean)\npublic  boolean "
-                        + "areNotificationIconsDisabled()\npublic  void "
-                        + "setNotificationIconsDisabled(boolean)\npublic  boolean "
+                        + "areSystemIconsDisabled()\npublic  void setSystemIconsDisabled(boolean)"
+                        + "\npublic  boolean isClockDisabled()\npublic  "
+                        + "void setClockDisabled(boolean)\npublic  "
+                        + "boolean areNotificationIconsDisabled()\npublic  "
+                        + "void setNotificationIconsDisabled(boolean)\npublic  boolean "
                         + "isNotificationTickerDisabled()\npublic  void "
                         + "setNotificationTickerDisabled(boolean)\npublic "
                         + "@android.annotation.TestApi boolean isRotationSuggestionDisabled()\n"
                         + "public  void setRotationSuggestionDisabled(boolean)\npublic "
-                        + "@android.annotation.SystemApi boolean areAllComponentsEnabled()\npublic"
-                        + "  void setEnableAll()\npublic  boolean areAllComponentsDisabled()\n"
-                        + "public  void setDisableAll()\npublic @android.annotation.NonNull "
+                        + "@android.annotation.SystemApi boolean areAllComponentsEnabled()\n"
+                        + "public  void setEnableAll()\npublic  boolean areAllComponentsDisabled()"
+                        + "\npublic  void setDisableAll()\npublic @android.annotation.NonNull "
                         + "@java.lang.Override java.lang.String toString()\npublic  "
                         + "android.util.Pair<java.lang.Integer,java.lang.Integer> toFlags()\n"
                         + "class DisableInfo extends java.lang.Object implements "
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index 6246dd7..91cdf8d 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -124,6 +124,22 @@
             "vcn_network_selection_ipsec_packet_loss_percent_threshold";
 
     /**
+     * Key for detecting unusually large increases in IPsec packet sequence numbers.
+     *
+     * <p>If the sequence number increases by more than this value within a second, it may indicate
+     * an intentional leap on the server's downlink. To avoid false positives, the packet loss
+     * detector will suppress loss reporting.
+     *
+     * <p>By default, there's no maximum limit enforced, prioritizing detection of lossy networks.
+     * To reduce false positives, consider setting an appropriate maximum threshold.
+     *
+     * @hide
+     */
+    @NonNull
+    public static final String VCN_NETWORK_SELECTION_MAX_SEQ_NUM_INCREASE_PER_SECOND_KEY =
+            "vcn_network_selection_max_seq_num_increase_per_second";
+
+    /**
      * Key for the list of timeouts in minute to stop penalizing an underlying network candidate
      *
      * @hide
@@ -180,6 +196,7 @@
                 VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY,
                 VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY,
                 VCN_NETWORK_SELECTION_IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_KEY,
+                VCN_NETWORK_SELECTION_MAX_SEQ_NUM_INCREASE_PER_SECOND_KEY,
                 VCN_NETWORK_SELECTION_PENALTY_TIMEOUT_MINUTES_LIST_KEY,
                 VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY,
                 VCN_SAFE_MODE_TIMEOUT_SECONDS_KEY,
diff --git a/core/java/android/net/vcn/flags.aconfig b/core/java/android/net/vcn/flags.aconfig
index e64823a..6fde398 100644
--- a/core/java/android/net/vcn/flags.aconfig
+++ b/core/java/android/net/vcn/flags.aconfig
@@ -34,4 +34,14 @@
     namespace: "vcn"
     description: "Re-evaluate IPsec packet loss on LinkProperties or NetworkCapabilities change"
     bug: "323238888"
+}
+
+flag{
+    name: "handle_seq_num_leap"
+    namespace: "vcn"
+    description: "Do not report bad network when there is a suspected sequence number leap"
+    bug: "332598276"
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
 }
\ No newline at end of file
diff --git a/core/java/android/security/FileIntegrityManager.java b/core/java/android/security/FileIntegrityManager.java
index 025aac9..478435b 100644
--- a/core/java/android/security/FileIntegrityManager.java
+++ b/core/java/android/security/FileIntegrityManager.java
@@ -20,6 +20,8 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.content.Context;
 import android.os.IInstalld.IFsveritySetupAuthToken;
@@ -99,8 +101,11 @@
      * @throws IOException If the operation failed.
      *
      * @see <a href="https://www.kernel.org/doc/html/next/filesystems/fsverity.html">Kernel doc</a>
+     * @hide
      */
     @FlaggedApi(Flags.FLAG_FSVERITY_API)
+    @SuppressLint("StreamFiles")
+    @SystemApi
     public void setupFsVerity(@NonNull File file) throws IOException {
         if (!file.isAbsolute()) {
             // fs-verity is to be enabled by installd, which enforces the validation to the
@@ -138,8 +143,11 @@
      * @param file The file to measure the fs-verity digest.
      * @return The fs-verity digest in byte[], null if none.
      * @see <a href="https://www.kernel.org/doc/html/next/filesystems/fsverity.html">Kernel doc</a>
+     * @hide
      */
     @FlaggedApi(Flags.FLAG_FSVERITY_API)
+    @SuppressLint("StreamFiles")
+    @SystemApi
     public @Nullable byte[] getFsVerityDigest(@NonNull File file) throws IOException {
         return VerityUtils.getFsverityDigest(file.getPath());
     }
diff --git a/core/java/android/tracing/perfetto/DataSource.java b/core/java/android/tracing/perfetto/DataSource.java
index c33fa7d..b9ab82c 100644
--- a/core/java/android/tracing/perfetto/DataSource.java
+++ b/core/java/android/tracing/perfetto/DataSource.java
@@ -18,10 +18,6 @@
 
 import android.util.proto.ProtoInputStream;
 
-import com.android.internal.annotations.VisibleForTesting;
-
-import dalvik.annotation.optimization.CriticalNative;
-
 /**
  * Templated base class meant to be derived by embedders to create a custom data
  * source.
@@ -73,7 +69,8 @@
      * @param fun The tracing lambda that will be called with the tracing contexts of each active
      *            tracing instance.
      */
-    public final void trace(TraceFunction<TlsStateType, IncrementalStateType> fun) {
+    public final void trace(
+            TraceFunction<DataSourceInstanceType, TlsStateType, IncrementalStateType> fun) {
         boolean startedIterator = nativePerfettoDsTraceIterateBegin(mNativeObj);
 
         if (!startedIterator) {
@@ -82,8 +79,10 @@
 
         try {
             do {
-                TracingContext<TlsStateType, IncrementalStateType> ctx =
-                        new TracingContext<>(mNativeObj);
+                int instanceIndex = nativeGetPerfettoDsInstanceIndex(mNativeObj);
+
+                TracingContext<DataSourceInstanceType, TlsStateType, IncrementalStateType> ctx =
+                        new TracingContext<>(this, instanceIndex);
                 fun.trace(ctx);
 
                 ctx.flush();
@@ -104,9 +103,7 @@
      * Override this method to create a custom TlsState object for your DataSource. A new instance
      * will be created per trace instance per thread.
      *
-     * NOTE: Should only be called from native side.
      */
-    @VisibleForTesting
     public TlsStateType createTlsState(CreateTlsStateArgs<DataSourceInstanceType> args) {
         return null;
     }
@@ -114,9 +111,8 @@
     /**
      * Override this method to create and use a custom IncrementalState object for your DataSource.
      *
-     * NOTE: Should only be called from native side.
      */
-    protected IncrementalStateType createIncrementalState(
+    public IncrementalStateType createIncrementalState(
             CreateIncrementalStateArgs<DataSourceInstanceType> args) {
         return null;
     }
@@ -179,10 +175,8 @@
     private static native void nativeReleasePerfettoInstanceLocked(
             long dataSourcePtr, int dsInstanceIdx);
 
-    @CriticalNative
     private static native boolean nativePerfettoDsTraceIterateBegin(long dataSourcePtr);
-    @CriticalNative
     private static native boolean nativePerfettoDsTraceIterateNext(long dataSourcePtr);
-    @CriticalNative
     private static native void nativePerfettoDsTraceIterateBreak(long dataSourcePtr);
+    private static native int nativeGetPerfettoDsInstanceIndex(long dataSourcePtr);
 }
diff --git a/core/java/android/tracing/perfetto/TraceFunction.java b/core/java/android/tracing/perfetto/TraceFunction.java
index 13e663d..d8854f9 100644
--- a/core/java/android/tracing/perfetto/TraceFunction.java
+++ b/core/java/android/tracing/perfetto/TraceFunction.java
@@ -19,12 +19,14 @@
 /**
  * The interface for the trace function called from native on a trace call with a context.
  *
+ * @param <DataSourceInstanceType> The type of DataSource this tracing context is for.
  * @param <TlsStateType> The type of the custom TLS state, if any is used.
  * @param <IncrementalStateType> The type of the custom incremental state, if any is used.
  *
  * @hide
  */
-public interface TraceFunction<TlsStateType, IncrementalStateType> {
+public interface TraceFunction<DataSourceInstanceType extends DataSourceInstance,
+        TlsStateType, IncrementalStateType> {
 
     /**
      * This function will be called synchronously (i.e., always before trace() returns) only if
@@ -34,5 +36,5 @@
      *
      * @param ctx the tracing context to trace for in the trace function.
      */
-    void trace(TracingContext<TlsStateType, IncrementalStateType> ctx);
+    void trace(TracingContext<DataSourceInstanceType, TlsStateType, IncrementalStateType> ctx);
 }
diff --git a/core/java/android/tracing/perfetto/TracingContext.java b/core/java/android/tracing/perfetto/TracingContext.java
index 060f964..6b7df54 100644
--- a/core/java/android/tracing/perfetto/TracingContext.java
+++ b/core/java/android/tracing/perfetto/TracingContext.java
@@ -24,18 +24,25 @@
 /**
  * Argument passed to the lambda function passed to Trace().
  *
+ * @param <DataSourceInstanceType> The type of the datasource this tracing context is for.
  * @param <TlsStateType> The type of the custom TLS state, if any is used.
  * @param <IncrementalStateType> The type of the custom incremental state, if any is used.
  *
  * @hide
  */
-public class TracingContext<TlsStateType, IncrementalStateType> {
+public class TracingContext<DataSourceInstanceType extends DataSourceInstance, TlsStateType,
+        IncrementalStateType> {
 
-    private final long mNativeDsPtr;
+    private final DataSource<DataSourceInstanceType, TlsStateType, IncrementalStateType>
+            mDataSource;
+    private final int mInstanceIndex;
     private final List<ProtoOutputStream> mTracePackets = new ArrayList<>();
 
-    TracingContext(long nativeDsPtr) {
-        this.mNativeDsPtr = nativeDsPtr;
+    TracingContext(DataSource<DataSourceInstanceType, TlsStateType, IncrementalStateType>
+            dataSource,
+            int instanceIndex) {
+        this.mDataSource = dataSource;
+        this.mInstanceIndex = instanceIndex;
     }
 
     /**
@@ -61,18 +68,26 @@
      * Stop timeout expires.
      */
     public void flush() {
-        nativeFlush(mNativeDsPtr, getAndClearAllPendingTracePackets());
+        nativeFlush(mDataSource.mNativeObj, getAndClearAllPendingTracePackets());
     }
 
     /**
      * Can optionally be used to store custom per-sequence
      * session data, which is not reset when incremental state is cleared
      * (e.g. configuration options).
-     *
+     *h
      * @return The TlsState instance for the tracing thread and instance.
      */
     public TlsStateType getCustomTlsState() {
-        return (TlsStateType) nativeGetCustomTls(mNativeDsPtr);
+        TlsStateType tlsState = (TlsStateType) nativeGetCustomTls(mDataSource.mNativeObj);
+        if (tlsState == null) {
+            final CreateTlsStateArgs<DataSourceInstanceType> args =
+                    new CreateTlsStateArgs<>(mDataSource, mInstanceIndex);
+            tlsState = mDataSource.createTlsState(args);
+            nativeSetCustomTls(mDataSource.mNativeObj, tlsState);
+        }
+
+        return tlsState;
     }
 
     /**
@@ -82,7 +97,16 @@
      * @return The current IncrementalState object instance.
      */
     public IncrementalStateType getIncrementalState() {
-        return (IncrementalStateType) nativeGetIncrementalState(mNativeDsPtr);
+        IncrementalStateType incrementalState =
+                (IncrementalStateType) nativeGetIncrementalState(mDataSource.mNativeObj);
+        if (incrementalState == null) {
+            final CreateIncrementalStateArgs<DataSourceInstanceType> args =
+                    new CreateIncrementalStateArgs<>(mDataSource, mInstanceIndex);
+            incrementalState = mDataSource.createIncrementalState(args);
+            nativeSetIncrementalState(mDataSource.mNativeObj, incrementalState);
+        }
+
+        return incrementalState;
     }
 
     private byte[][] getAndClearAllPendingTracePackets() {
@@ -97,6 +121,10 @@
     }
 
     private static native void nativeFlush(long dataSourcePtr, byte[][] packetData);
+
     private static native Object nativeGetCustomTls(long nativeDsPtr);
+    private static native void nativeSetCustomTls(long nativeDsPtr, Object tlsState);
+
     private static native Object nativeGetIncrementalState(long nativeDsPtr);
+    private static native void nativeSetIncrementalState(long nativeDsPtr, Object incrementalState);
 }
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index c95d6ff..a23df79 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -951,7 +951,7 @@
 
     private boolean performSurfaceTransaction(ViewRootImpl viewRoot, Translator translator,
             boolean creating, boolean sizeChanged, boolean hintChanged, boolean relativeZChanged,
-            Transaction surfaceUpdateTransaction) {
+            boolean hdrHeadroomChanged, Transaction surfaceUpdateTransaction) {
         boolean realSizeChanged = false;
 
         mSurfaceLock.lock();
@@ -986,7 +986,7 @@
 
             updateBackgroundVisibility(surfaceUpdateTransaction);
             updateBackgroundColor(surfaceUpdateTransaction);
-            if (mLimitedHdrEnabled) {
+            if (mLimitedHdrEnabled && hdrHeadroomChanged) {
                 surfaceUpdateTransaction.setDesiredHdrHeadroom(
                         mBlastSurfaceControl, mHdrHeadroom);
             }
@@ -1203,7 +1203,7 @@
                 }
 
                 final boolean realSizeChanged = performSurfaceTransaction(viewRoot, translator,
-                        creating, sizeChanged, hintChanged, relativeZChanged,
+                        creating, sizeChanged, hintChanged, relativeZChanged, hdrHeadroomChanged,
                         surfaceUpdateTransaction);
 
                 try {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index cacf0d2..ac1f646 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2428,6 +2428,26 @@
      */
     public static final int FRAME_RATE_CATEGORY_REASON_IDLE = 0x0700_0000;
 
+    /**
+     * This indicates that the frame rate category was chosen because it is currently boosting.
+     * @hide
+     */
+    public static final int FRAME_RATE_CATEGORY_REASON_BOOST = 0x0800_0000;
+
+    /**
+     * This indicates that the frame rate category was chosen because it is currently having
+     * touch boost.
+     * @hide
+     */
+    public static final int FRAME_RATE_CATEGORY_REASON_TOUCH = 0x0900_0000;
+
+    /**
+     * This indicates that the frame rate category was chosen because it is currently having
+     * touch boost.
+     * @hide
+     */
+    public static final int FRAME_RATE_CATEGORY_REASON_CONFLICTED = 0x0A00_0000;
+
     private static final int FRAME_RATE_CATEGORY_REASON_MASK = 0xFFFF_0000;
 
     /**
@@ -5742,7 +5762,7 @@
      */
     private static final float FRAME_RATE_SIZE_PERCENTAGE_THRESHOLD = 0.07f;
 
-    private static final float MAX_FRAME_RATE = 140;
+    static final float MAX_FRAME_RATE = 140;
 
     private static final int INFREQUENT_UPDATE_INTERVAL_MILLIS = 100;
     private static final int INFREQUENT_UPDATE_COUNTS = 2;
@@ -33897,36 +33917,41 @@
         int height = mBottom - mTop;
 
         if (viewRootImpl != null && (width != 0 && height != 0)) {
-            if (mAttachInfo.mViewVelocityApi) {
-                float velocity = mFrameContentVelocity;
-                int mask = PFLAG4_HAS_MOVED | PFLAG4_HAS_DRAWN;
-                float frameRate = 0;
+            if (viewRootImpl.shouldCheckFrameRate(mPreferredFrameRate > 0f)) {
+                float velocityFrameRate = 0f;
+                if (mAttachInfo.mViewVelocityApi) {
+                    float velocity = mFrameContentVelocity;
 
-                if (velocity < 0f
-                        && (mPrivateFlags4 & mask) == mask
-                        && mParent instanceof View
-                        && ((View) mParent).mFrameContentVelocity <= 0
-                ) {
-                    // This current calculation is very simple. If something on the screen moved,
-                    // then it votes for the highest velocity. If it doesn't move, then return 0.
-                    velocity = Float.POSITIVE_INFINITY;
-                    frameRate = MAX_FRAME_RATE;
-                }
-                if (velocity > 0f) {
-                    if (sToolkitFrameRateVelocityMappingReadOnlyFlagValue) {
-                        frameRate = convertVelocityToFrameRate(velocity);
+                    if (velocity < 0f
+                            && (mPrivateFlags4 & (PFLAG4_HAS_MOVED | PFLAG4_HAS_DRAWN)) == (
+                            PFLAG4_HAS_MOVED | PFLAG4_HAS_DRAWN)
+                            && mParent instanceof View
+                            && ((View) mParent).mFrameContentVelocity <= 0
+                    ) {
+                        // This current calculation is very simple. If something on the screen
+                        // moved, then it votes for the highest velocity.
+                        velocityFrameRate = MAX_FRAME_RATE;
+                    } else if (velocity > 0f) {
+                        velocityFrameRate = convertVelocityToFrameRate(velocity);
                     }
-                    viewRootImpl.votePreferredFrameRate(frameRate, FRAME_RATE_COMPATIBILITY_GTE);
-                    return;
+                }
+                if (velocityFrameRate > 0f || mPreferredFrameRate > 0f) {
+                    int compatibility = FRAME_RATE_COMPATIBILITY_GTE;
+                    float frameRate = velocityFrameRate;
+                    if (mPreferredFrameRate > velocityFrameRate) {
+                        compatibility = FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
+                        frameRate = mPreferredFrameRate;
+                    }
+                    viewRootImpl.votePreferredFrameRate(frameRate, compatibility);
                 }
             }
-            if (!willNotDraw() && isDirty()) {
+            if (!willNotDraw() && isDirty() && viewRootImpl.shouldCheckFrameRateCategory()) {
                 if (sToolkitMetricsForFrameRateDecisionFlagValue) {
                     float sizePercentage = width * height / mAttachInfo.mDisplayPixelCount;
                     viewRootImpl.recordViewPercentage(sizePercentage);
                 }
 
-                int frameRateCategory;
+                int frameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
                 if (Float.isNaN(mPreferredFrameRate)) {
                     frameRateCategory = calculateFrameRateCategory();
                 } else if (mPreferredFrameRate < 0) {
@@ -33951,10 +33976,6 @@
                                     | FRAME_RATE_CATEGORY_REASON_INVALID;
                         }
                     }
-                } else {
-                    viewRootImpl.votePreferredFrameRate(mPreferredFrameRate,
-                            mFrameRateCompatibility);
-                    return;
                 }
 
                 int category = frameRateCategory & ~FRAME_RATE_CATEGORY_REASON_MASK;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 4024e3c..09f98d7 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -35,14 +35,18 @@
 import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
 import static android.view.Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
 import static android.view.Surface.FRAME_RATE_COMPATIBILITY_GTE;
+import static android.view.View.FRAME_RATE_CATEGORY_REASON_BOOST;
+import static android.view.View.FRAME_RATE_CATEGORY_REASON_CONFLICTED;
 import static android.view.View.FRAME_RATE_CATEGORY_REASON_IDLE;
 import static android.view.View.FRAME_RATE_CATEGORY_REASON_INTERMITTENT;
 import static android.view.View.FRAME_RATE_CATEGORY_REASON_INVALID;
 import static android.view.View.FRAME_RATE_CATEGORY_REASON_LARGE;
 import static android.view.View.FRAME_RATE_CATEGORY_REASON_REQUESTED;
 import static android.view.View.FRAME_RATE_CATEGORY_REASON_SMALL;
+import static android.view.View.FRAME_RATE_CATEGORY_REASON_TOUCH;
 import static android.view.View.FRAME_RATE_CATEGORY_REASON_UNKNOWN;
 import static android.view.View.FRAME_RATE_CATEGORY_REASON_VELOCITY;
+import static android.view.View.MAX_FRAME_RATE;
 import static android.view.View.PFLAG_DRAW_ANIMATION;
 import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
 import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
@@ -235,6 +239,7 @@
 import android.view.autofill.AutofillManager;
 import android.view.contentcapture.ContentCaptureManager;
 import android.view.contentcapture.ContentCaptureSession;
+import android.view.flags.Flags;
 import android.view.inputmethod.ImeTracker;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.Scroller;
@@ -1149,7 +1154,9 @@
     private static boolean sToolkitFrameRateTypingReadOnlyFlagValue;
     private static final boolean sToolkitFrameRateViewEnablingReadOnlyFlagValue;
     private static boolean sToolkitFrameRateVelocityMappingReadOnlyFlagValue =
-            toolkitFrameRateVelocityMappingReadOnly();;
+            toolkitFrameRateVelocityMappingReadOnly();
+    private static boolean sToolkitEnableInvalidateCheckThreadFlagValue =
+            Flags.enableInvalidateCheckThread();
 
     static {
         sToolkitSetFrameRateReadOnlyFlagValue = toolkitSetFrameRateReadOnly();
@@ -2371,8 +2378,9 @@
 
     @Override
     public void onDescendantInvalidated(@NonNull View child, @NonNull View descendant) {
-        // TODO: Re-enable after camera is fixed or consider targetSdk checking this
-        // checkThread();
+        if (sToolkitEnableInvalidateCheckThreadFlagValue) {
+            checkThread();
+        }
         if ((descendant.mPrivateFlags & PFLAG_DRAW_ANIMATION) != 0) {
             mIsAnimating = true;
         }
@@ -4191,8 +4199,15 @@
         // For the variable refresh rate project.
         // We set the preferred frame rate and frame rate category at the end of performTraversals
         // when the values are applicable.
+        setCategoryFromCategoryCounts();
         setPreferredFrameRate(mPreferredFrameRate);
         setPreferredFrameRateCategory(mPreferredFrameRateCategory);
+        if (!mIsFrameRateConflicted) {
+            mHandler.removeMessages(MSG_FRAME_RATE_SETTING);
+            mHandler.sendEmptyMessageDelayed(MSG_FRAME_RATE_SETTING,
+                    FRAME_RATE_SETTING_REEVALUATE_TIME);
+        }
+        checkIdleness();
         mFrameRateCategoryHighCount = mFrameRateCategoryHighCount > 0
                 ? mFrameRateCategoryHighCount - 1 : mFrameRateCategoryHighCount;
         mFrameRateCategoryNormalCount = mFrameRateCategoryNormalCount > 0
@@ -4201,7 +4216,6 @@
                 ? mFrameRateCategoryLowCount - 1 : mFrameRateCategoryLowCount;
         mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_DEFAULT;
         mPreferredFrameRate = -1;
-        mFrameRateCompatibility = FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
         mIsFrameRateConflicted = false;
         mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_UNKNOWN;
     }
@@ -6630,8 +6644,6 @@
                      */
                     mIsFrameRateBoosting = false;
                     mIsTouchBoosting = false;
-                    setPreferredFrameRateCategory(Math.max(mPreferredFrameRateCategory,
-                            mLastPreferredFrameRateCategory));
                     break;
                 case MSG_CHECK_INVALIDATION_IDLE:
                     if (!mHasInvalidation && !mIsFrameRateBoosting && !mIsTouchBoosting) {
@@ -7677,7 +7689,6 @@
                     mWindowAttributes.type)) {
                 // set the frame rate to the maximum value.
                 mIsTouchBoosting = true;
-                setPreferredFrameRateCategory(mPreferredFrameRateCategory);
             }
             /**
              * We want to lower the refresh rate when MotionEvent.ACTION_UP,
@@ -12469,59 +12480,50 @@
         EventLog.writeEvent(LOGTAG_VIEWROOT_DRAW_EVENT, mTag, msg);
     }
 
+    /**
+     * Sets the mPreferredFrameRateCategory from the high, high_hint, normal, and low counts.
+     */
+    private void setCategoryFromCategoryCounts() {
+        if (mFrameRateCategoryHighCount > 0) {
+            mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_HIGH;
+        } else if (mFrameRateCategoryHighHintCount > 0) {
+            mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_HIGH_HINT;
+        } else if (mFrameRateCategoryNormalCount > 0) {
+            mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NORMAL;
+        } else if (mFrameRateCategoryLowCount > 0) {
+            mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_LOW;
+        }
+    }
+
     private void setPreferredFrameRateCategory(int preferredFrameRateCategory) {
-        if (!shouldSetFrameRateCategory()
-                || (mFrameRateCompatibility == FRAME_RATE_COMPATIBILITY_GTE
-                && mPreferredFrameRate > 0
-                && sToolkitFrameRateVelocityMappingReadOnlyFlagValue)) {
+        if (!shouldSetFrameRateCategory()) {
             return;
         }
-        int categoryFromConflictedFrameRates = FRAME_RATE_CATEGORY_DEFAULT;
-        if (mIsFrameRateConflicted) {
-            categoryFromConflictedFrameRates = mPreferredFrameRate > 60
-                    ? FRAME_RATE_CATEGORY_HIGH : FRAME_RATE_CATEGORY_NORMAL;
-        }
 
-        int frameRateCategory = mIsTouchBoosting
-                ? FRAME_RATE_CATEGORY_HIGH_HINT
-                : Math.max(preferredFrameRateCategory, categoryFromConflictedFrameRates);
+        int frameRateCategory;
+        int frameRateReason;
+        String view;
 
-        // FRAME_RATE_CATEGORY_HIGH has a higher precedence than FRAME_RATE_CATEGORY_HIGH_HINT
-        // For now, FRAME_RATE_CATEGORY_HIGH_HINT is used for boosting with user interaction.
-        // FRAME_RATE_CATEGORY_HIGH is for boosting without user interaction
-        // (e.g., Window Initialization).
-        if (mIsFrameRateBoosting || mInsetsAnimationRunning
-                || (mFrameRateCompatibility == FRAME_RATE_COMPATIBILITY_GTE
-                        && mPreferredFrameRate > 0)) {
+        if (mIsFrameRateBoosting || mInsetsAnimationRunning) {
             frameRateCategory = FRAME_RATE_CATEGORY_HIGH;
-            if (mFrameRateCompatibility == FRAME_RATE_COMPATIBILITY_GTE) {
-                // We've received a velocity, so we'll let the velocity control the
-                // frame rate unless we receive additional motion events.
-                mIsTouchBoosting = false;
-                mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_VELOCITY;
-                mFrameRateCategoryView = null;
-            } else {
-                mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_UNKNOWN;
-            }
+            frameRateReason = FRAME_RATE_CATEGORY_REASON_BOOST;
+            view = null;
+        } else if (mIsTouchBoosting && preferredFrameRateCategory < FRAME_RATE_CATEGORY_HIGH_HINT) {
+            frameRateCategory = FRAME_RATE_CATEGORY_HIGH_HINT;
+            frameRateReason = FRAME_RATE_CATEGORY_REASON_TOUCH;
+            view = null;
+        } else {
+            frameRateCategory = preferredFrameRateCategory;
+            frameRateReason = mFrameRateCategoryChangeReason;
+            view = mFrameRateCategoryView;
         }
 
         try {
             if (frameRateCategory != FRAME_RATE_CATEGORY_DEFAULT
                     && mLastPreferredFrameRateCategory != frameRateCategory) {
                 if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
-                    String reason = reasonToString(mFrameRateCategoryChangeReason);
-                    String sourceView = mFrameRateCategoryView == null ? "-"
-                            : mFrameRateCategoryView;
-                    if (preferredFrameRateCategory == FRAME_RATE_CATEGORY_HIGH_HINT) {
-                        reason = "touch boost";
-                        sourceView = "-";
-                    } else if (categoryFromConflictedFrameRates == frameRateCategory
-                            && frameRateCategory != preferredFrameRateCategory
-                            && mIsFrameRateConflicted
-                    ) {
-                        reason = "conflict";
-                        sourceView = "-";
-                    }
+                    String reason = reasonToString(frameRateReason);
+                    String sourceView = view == null ? "-" : view;
                     String category = categoryToString(frameRateCategory);
                     Trace.traceBegin(
                             Trace.TRACE_TAG_VIEW, "ViewRootImpl#setFrameRateCategory "
@@ -12565,24 +12567,21 @@
             case FRAME_RATE_CATEGORY_REASON_VELOCITY -> str = "velocity";
             case FRAME_RATE_CATEGORY_REASON_IDLE -> str = "idle";
             case FRAME_RATE_CATEGORY_REASON_UNKNOWN -> str = "unknown";
+            case FRAME_RATE_CATEGORY_REASON_BOOST -> str = "boost";
+            case FRAME_RATE_CATEGORY_REASON_TOUCH -> str = "touch";
+            case FRAME_RATE_CATEGORY_REASON_CONFLICTED -> str = "conflicted";
             default -> str = String.valueOf(reason);
         }
         return str;
     }
 
     private void setPreferredFrameRate(float preferredFrameRate) {
-        if (!shouldSetFrameRate()) {
-            return;
-        }
-        if (mFrameRateCompatibility == FRAME_RATE_COMPATIBILITY_GTE
-                && preferredFrameRate > 0 && !sToolkitFrameRateVelocityMappingReadOnlyFlagValue) {
-            mIsTouchBoosting = false;
+        if (!shouldSetFrameRate() || preferredFrameRate < 0) {
             return;
         }
 
         try {
-            if (mLastPreferredFrameRate != preferredFrameRate
-                    && preferredFrameRate >= 0) {
+            if (mLastPreferredFrameRate != preferredFrameRate) {
                 if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
                     Trace.traceBegin(
                             Trace.TRACE_TAG_VIEW, "ViewRootImpl#setFrameRate "
@@ -12591,7 +12590,7 @@
                 }
                 if (sToolkitFrameRateFunctionEnablingReadOnlyFlagValue) {
                     mFrameRateTransaction.setFrameRate(mSurfaceControl, preferredFrameRate,
-                    mFrameRateCompatibility).applyAsyncUnsafe();
+                            mFrameRateCompatibility).applyAsyncUnsafe();
                 }
                 mLastPreferredFrameRate = preferredFrameRate;
             }
@@ -12602,12 +12601,6 @@
         }
     }
 
-    private void sendDelayedEmptyMessage(int message, int delayedTime) {
-        mHandler.removeMessages(message);
-
-        mHandler.sendEmptyMessageDelayed(message, delayedTime);
-    }
-
     private boolean shouldSetFrameRateCategory() {
         // use toolkitSetFrameRate flag to gate the change
         return  mSurface.isValid() && shouldEnableDvrr();
@@ -12645,28 +12638,34 @@
             case FRAME_RATE_CATEGORY_HIGH ->
                     mFrameRateCategoryHighCount = FRAME_RATE_CATEGORY_COUNT;
         }
-
-        int oldCategory = mPreferredFrameRateCategory;
-        // For View that votes NO_PREFERENCE
-        mPreferredFrameRateCategory = frameRateCategory;
-
-        if (mFrameRateCategoryHighCount > 0) {
-            mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_HIGH;
-        } else if (mFrameRateCategoryHighHintCount > 0) {
-            mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_HIGH_HINT;
-        } else if (mFrameRateCategoryNormalCount > 0) {
-            mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NORMAL;
-        } else if (mFrameRateCategoryLowCount > 0) {
-            mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_LOW;
+        if (frameRateCategory > mPreferredFrameRateCategory) {
+            mPreferredFrameRateCategory = frameRateCategory;
+            mFrameRateCategoryChangeReason = reason;
+            mFrameRateCategoryView = view == null ? "-" : view.getClass().getSimpleName();
         }
         mHasInvalidation = true;
-        checkIdleness();
-        if (mPreferredFrameRateCategory != oldCategory
-                && mPreferredFrameRateCategory == frameRateCategory
-        ) {
-            mFrameRateCategoryChangeReason = reason;
-            mFrameRateCategoryView = view == null ? "null" : view.getClass().getSimpleName();
-        }
+    }
+
+    /**
+     * Returns whether a View should vote for frame rate category. When the category is HIGH
+     * already, there's no need to calculate the category on the View and vote.
+     */
+    public boolean shouldCheckFrameRateCategory() {
+        return mPreferredFrameRateCategory < FRAME_RATE_CATEGORY_HIGH;
+    }
+
+    /**
+     * Returns whether a View should vote for frame rate. When the maximum frame rate has already
+     * been voted for, there's no point in calculating and voting for the frame rate. When
+     * isDirect is false, then it will return false when the velocity-calculated frame rate
+     * can be avoided.
+     * @param isDirect true when the frame rate has been set directly on the View or false if
+     *                 the calculation is based only on velocity.
+     */
+    public boolean shouldCheckFrameRate(boolean isDirect) {
+        return mPreferredFrameRate < MAX_FRAME_RATE
+                || (!isDirect && !sToolkitFrameRateVelocityMappingReadOnlyFlagValue
+                && mPreferredFrameRateCategory < FRAME_RATE_CATEGORY_HIGH);
     }
 
     /**
@@ -12692,24 +12691,44 @@
         if (frameRate <= 0) {
             return;
         }
+        if (frameRateCompatibility == FRAME_RATE_COMPATIBILITY_GTE) {
+            mIsTouchBoosting = false;
+            if (!sToolkitFrameRateVelocityMappingReadOnlyFlagValue) {
+                mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_HIGH;
+                mFrameRateCategoryHighCount = FRAME_RATE_CATEGORY_COUNT;
+                mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_VELOCITY;
+                mFrameRateCategoryView = null;
+                return;
+            }
+        }
+        float nextFrameRate;
+        int nextFrameRateCompatibility;
+        if (frameRate > mPreferredFrameRate) {
+            nextFrameRate = frameRate;
+            nextFrameRateCompatibility = frameRateCompatibility;
+        } else {
+            nextFrameRate = mPreferredFrameRate;
+            nextFrameRateCompatibility = mFrameRateCompatibility;
+        }
+
         if (mPreferredFrameRate > 0 && mPreferredFrameRate % frameRate != 0
                 && frameRate % mPreferredFrameRate != 0) {
             mIsFrameRateConflicted = true;
-            mFrameRateCompatibility = FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
-        }
-        if (frameRate > mPreferredFrameRate) {
-            mFrameRateCompatibility = frameRateCompatibility;
+            if (nextFrameRate > 60 && mFrameRateCategoryHighCount != FRAME_RATE_CATEGORY_COUNT) {
+                mFrameRateCategoryHighCount = FRAME_RATE_CATEGORY_COUNT;
+                mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_CONFLICTED;
+                mFrameRateCategoryView = null;
+            } else if (mFrameRateCategoryHighCount == 0 && mFrameRateCategoryHighHintCount == 0
+                    && mFrameRateCategoryNormalCount < FRAME_RATE_CATEGORY_COUNT) {
+                mFrameRateCategoryNormalCount = FRAME_RATE_CATEGORY_COUNT;
+                mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_CONFLICTED;
+                mFrameRateCategoryView = null;
+            }
         }
 
-        mPreferredFrameRate = Math.max(mPreferredFrameRate, frameRate);
+        mPreferredFrameRate = nextFrameRate;
+        mFrameRateCompatibility = nextFrameRateCompatibility;
         mHasInvalidation = true;
-
-        if (!mIsFrameRateConflicted) {
-            mHandler.removeMessages(MSG_FRAME_RATE_SETTING);
-            mHandler.sendEmptyMessageDelayed(MSG_FRAME_RATE_SETTING,
-                    FRAME_RATE_SETTING_REEVALUATE_TIME);
-        }
-        checkIdleness();
     }
 
     /**
@@ -12779,7 +12798,6 @@
 
     private void boostFrameRate(int boostTimeOut) {
         mIsFrameRateBoosting = true;
-        setPreferredFrameRateCategory(mPreferredFrameRateCategory);
         mHandler.removeMessages(MSG_TOUCH_BOOST_TIMEOUT);
         mHandler.sendEmptyMessageDelayed(MSG_TOUCH_BOOST_TIMEOUT,
                 boostTimeOut);
diff --git a/core/java/android/view/flags/view_flags.aconfig b/core/java/android/view/flags/view_flags.aconfig
index c482f8b..486c2ab 100644
--- a/core/java/android/view/flags/view_flags.aconfig
+++ b/core/java/android/view/flags/view_flags.aconfig
@@ -50,3 +50,11 @@
     description: "Enable default arrow icon when hovering on buttons or clickable widgets."
     bug: "299269803"
 }
+
+flag {
+    name: "enable_invalidate_check_thread"
+    namespace: "toolkit"
+    description: "Enable checkThread call in ViewRootImpl#onDescendentInvalidated"
+    bug: "333752000"
+    is_fixed_read_only: true
+}
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index 9524a6e..65e5f1a 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -49,3 +49,10 @@
     description: "Shows running apps in Desktop Mode Taskbar"
     bug: "332504528"
 }
+
+flag {
+    name: "enable_desktop_windowing_wallpaper_activity"
+    namespace: "lse_desktop_experience"
+    description: "Enables desktop wallpaper activity to show wallpaper in the desktop mode"
+    bug: "309014605"
+}
diff --git a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
index fca4e91..b6558cb 100644
--- a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
@@ -440,6 +440,7 @@
     }
 
     private int internStacktraceString(TracingContext<
+            ProtoLogDataSource.Instance,
             ProtoLogDataSource.TlsState,
             ProtoLogDataSource.IncrementalState> ctx,
             String stacktrace) {
@@ -449,7 +450,8 @@
     }
 
     private int internStringArg(
-            TracingContext<ProtoLogDataSource.TlsState, ProtoLogDataSource.IncrementalState> ctx,
+            TracingContext<ProtoLogDataSource.Instance, ProtoLogDataSource.TlsState,
+                    ProtoLogDataSource.IncrementalState> ctx,
             String string
     ) {
         final ProtoLogDataSource.IncrementalState incrementalState = ctx.getIncrementalState();
@@ -458,7 +460,8 @@
     }
 
     private int internString(
-            TracingContext<ProtoLogDataSource.TlsState, ProtoLogDataSource.IncrementalState> ctx,
+            TracingContext<ProtoLogDataSource.Instance, ProtoLogDataSource.TlsState,
+                    ProtoLogDataSource.IncrementalState> ctx,
             Map<String, Integer> internMap,
             long fieldId,
             String string
diff --git a/core/jni/android_tracing_PerfettoDataSource.cpp b/core/jni/android_tracing_PerfettoDataSource.cpp
index 5c7b470..f82ebfe 100644
--- a/core/jni/android_tracing_PerfettoDataSource.cpp
+++ b/core/jni/android_tracing_PerfettoDataSource.cpp
@@ -93,49 +93,6 @@
     return instance;
 }
 
-jobject PerfettoDataSource::createTlsStateGlobalRef(JNIEnv* env, PerfettoDsInstanceIndex inst_id) {
-    ScopedLocalRef<jobject> args(env,
-                                 env->NewObject(gCreateTlsStateArgsClassInfo.clazz,
-                                                gCreateTlsStateArgsClassInfo.init, mJavaDataSource,
-                                                inst_id));
-
-    ScopedLocalRef<jobject> tslState(env,
-                                     env->CallObjectMethod(mJavaDataSource,
-                                                           gPerfettoDataSourceClassInfo
-                                                                   .createTlsState,
-                                                           args.get()));
-
-    if (env->ExceptionCheck()) {
-        LOGE_EX(env);
-        env->ExceptionClear();
-        LOG_ALWAYS_FATAL("Failed to create new Java Perfetto incremental state");
-    }
-
-    return env->NewGlobalRef(tslState.get());
-}
-
-jobject PerfettoDataSource::createIncrementalStateGlobalRef(JNIEnv* env,
-                                                            PerfettoDsInstanceIndex inst_id) {
-    ScopedLocalRef<jobject> args(env,
-                                 env->NewObject(gCreateIncrementalStateArgsClassInfo.clazz,
-                                                gCreateIncrementalStateArgsClassInfo.init,
-                                                mJavaDataSource, inst_id));
-
-    ScopedLocalRef<jobject> incrementalState(env,
-                                             env->CallObjectMethod(mJavaDataSource,
-                                                                   gPerfettoDataSourceClassInfo
-                                                                           .createIncrementalState,
-                                                                   args.get()));
-
-    if (env->ExceptionCheck()) {
-        LOGE_EX(env);
-        env->ExceptionClear();
-        LOG_ALWAYS_FATAL("Failed to create Java Perfetto incremental state");
-    }
-
-    return env->NewGlobalRef(incrementalState.get());
-}
-
 bool PerfettoDataSource::TraceIterateBegin() {
     if (gInIteration) {
         return false;
@@ -177,6 +134,15 @@
     gInIteration = false;
 }
 
+PerfettoDsInstanceIndex PerfettoDataSource::GetInstanceIndex() {
+    if (!gInIteration) {
+        LOG_ALWAYS_FATAL("Tried calling GetInstanceIndex outside of a tracer iteration.");
+        return -1;
+    }
+
+    return gIterator.impl.inst_id;
+}
+
 jobject PerfettoDataSource::GetCustomTls() {
     if (!gInIteration) {
         LOG_ALWAYS_FATAL("Tried getting CustomTls outside of a tracer iteration.");
@@ -189,6 +155,18 @@
     return tls_state->jobj;
 }
 
+void PerfettoDataSource::SetCustomTls(jobject tlsState) {
+    if (!gInIteration) {
+        LOG_ALWAYS_FATAL("Tried getting CustomTls outside of a tracer iteration.");
+        return;
+    }
+
+    TlsState* tls_state =
+            reinterpret_cast<TlsState*>(PerfettoDsGetCustomTls(&dataSource, &gIterator));
+
+    tls_state->jobj = tlsState;
+}
+
 jobject PerfettoDataSource::GetIncrementalState() {
     if (!gInIteration) {
         LOG_ALWAYS_FATAL("Tried getting IncrementalState outside of a tracer iteration.");
@@ -201,6 +179,18 @@
     return incr_state->jobj;
 }
 
+void PerfettoDataSource::SetIncrementalState(jobject incrementalState) {
+    if (!gInIteration) {
+        LOG_ALWAYS_FATAL("Tried getting IncrementalState outside of a tracer iteration.");
+        return;
+    }
+
+    IncrementalState* incr_state = reinterpret_cast<IncrementalState*>(
+            PerfettoDsGetIncrementalState(&dataSource, &gIterator));
+
+    incr_state->jobj = incrementalState;
+}
+
 void PerfettoDataSource::WritePackets(JNIEnv* env, jobjectArray packets) {
     if (!gInIteration) {
         LOG_ALWAYS_FATAL("Tried writing packets outside of a tracer iteration.");
@@ -211,7 +201,7 @@
     for (int i = 0; i < packets_count; i++) {
         jbyteArray packet_proto_buffer = (jbyteArray)env->GetObjectArrayElement(packets, i);
 
-        jbyte* raw_proto_buffer = env->GetByteArrayElements(packet_proto_buffer, 0);
+        jbyte* raw_proto_buffer = env->GetByteArrayElements(packet_proto_buffer, nullptr);
         int buffer_size = env->GetArrayLength(packet_proto_buffer);
 
         struct PerfettoDsRootTracePacket trace_packet;
@@ -219,6 +209,8 @@
         PerfettoPbMsgAppendBytes(&trace_packet.msg.msg, (const uint8_t*)raw_proto_buffer,
                                  buffer_size);
         PerfettoDsTracerPacketEnd(&gIterator, &trace_packet);
+
+        env->ReleaseByteArrayElements(packet_proto_buffer, raw_proto_buffer, 0 /* default mode */);
     }
 }
 
@@ -264,7 +256,7 @@
 }
 
 void nativeRegisterDataSource(JNIEnv* env, jclass clazz, jlong datasource_ptr,
-                              int buffer_exhausted_policy) {
+                              jint buffer_exhausted_policy) {
     sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(datasource_ptr);
 
     struct PerfettoDsParams params = PerfettoDsParamsDefault();
@@ -291,13 +283,8 @@
 
     params.on_create_tls_cb = [](struct PerfettoDsImpl* ds_impl, PerfettoDsInstanceIndex inst_id,
                                  struct PerfettoDsTracerImpl* tracer, void* user_arg) -> void* {
-        JNIEnv* env = GetOrAttachJNIEnvironment(gVm, JNI_VERSION_1_6);
-
-        auto* datasource = reinterpret_cast<PerfettoDataSource*>(user_arg);
-
-        jobject java_tls_state = datasource->createTlsStateGlobalRef(env, inst_id);
-
-        auto* tls_state = new TlsState(java_tls_state);
+        // Populated later and only if required by the java side
+        auto* tls_state = new TlsState(NULL);
         return static_cast<void*>(tls_state);
     };
 
@@ -306,18 +293,16 @@
 
         TlsState* tls_state = reinterpret_cast<TlsState*>(ptr);
 
-        env->DeleteGlobalRef(tls_state->jobj);
+        if (tls_state->jobj != NULL) {
+            env->DeleteGlobalRef(tls_state->jobj);
+        }
         delete tls_state;
     };
 
     params.on_create_incr_cb = [](struct PerfettoDsImpl* ds_impl, PerfettoDsInstanceIndex inst_id,
                                   struct PerfettoDsTracerImpl* tracer, void* user_arg) -> void* {
-        JNIEnv* env = GetOrAttachJNIEnvironment(gVm, JNI_VERSION_1_6);
-
-        auto* datasource = reinterpret_cast<PerfettoDataSource*>(user_arg);
-        jobject java_incr_state = datasource->createIncrementalStateGlobalRef(env, inst_id);
-
-        auto* incr_state = new IncrementalState(java_incr_state);
+        // Populated later and only if required by the java side
+        auto* incr_state = new IncrementalState(NULL);
         return static_cast<void*>(incr_state);
     };
 
@@ -326,7 +311,9 @@
 
         IncrementalState* incr_state = reinterpret_cast<IncrementalState*>(ptr);
 
-        env->DeleteGlobalRef(incr_state->jobj);
+        if (incr_state->jobj != NULL) {
+            env->DeleteGlobalRef(incr_state->jobj);
+        }
         delete incr_state;
     };
 
@@ -386,31 +373,49 @@
     PerfettoDsImplReleaseInstanceLocked(datasource->dataSource.impl, instance_idx);
 }
 
-bool nativePerfettoDsTraceIterateBegin(jlong dataSourcePtr) {
+bool nativePerfettoDsTraceIterateBegin(JNIEnv* /* env */, jclass /* clazz */, jlong dataSourcePtr) {
     sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(dataSourcePtr);
     return datasource->TraceIterateBegin();
 }
 
-bool nativePerfettoDsTraceIterateNext(jlong dataSourcePtr) {
+bool nativePerfettoDsTraceIterateNext(JNIEnv* /* env */, jclass /* clazz */, jlong dataSourcePtr) {
     sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(dataSourcePtr);
     return datasource->TraceIterateNext();
 }
 
-void nativePerfettoDsTraceIterateBreak(jlong dataSourcePtr) {
+void nativePerfettoDsTraceIterateBreak(JNIEnv* /* env */, jclass /* clazz */, jlong dataSourcePtr) {
     sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(dataSourcePtr);
     return datasource->TraceIterateBreak();
 }
 
+jint nativeGetPerfettoDsInstanceIndex(JNIEnv* /* env */, jclass /* clazz */, jlong dataSourcePtr) {
+    sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(dataSourcePtr);
+    return (jint)datasource->GetInstanceIndex();
+}
+
 jobject nativeGetCustomTls(JNIEnv* /* env */, jclass /* clazz */, jlong dataSourcePtr) {
     sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(dataSourcePtr);
     return datasource->GetCustomTls();
 }
 
+void nativeSetCustomTls(JNIEnv* env, jclass /* clazz */, jlong dataSourcePtr, jobject tlsState) {
+    sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(dataSourcePtr);
+    tlsState = env->NewGlobalRef(tlsState);
+    return datasource->SetCustomTls(tlsState);
+}
+
 jobject nativeGetIncrementalState(JNIEnv* /* env */, jclass /* clazz */, jlong dataSourcePtr) {
     sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(dataSourcePtr);
     return datasource->GetIncrementalState();
 }
 
+void nativeSetIncrementalState(JNIEnv* env, jclass /* clazz */, jlong dataSourcePtr,
+                               jobject incrementalState) {
+    sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(dataSourcePtr);
+    incrementalState = env->NewGlobalRef(incrementalState);
+    return datasource->SetIncrementalState(incrementalState);
+}
+
 const JNINativeMethod gMethods[] = {
         /* name, signature, funcPtr */
         {"nativeCreate", "(Landroid/tracing/perfetto/DataSource;Ljava/lang/String;)J",
@@ -425,13 +430,16 @@
 
         {"nativePerfettoDsTraceIterateBegin", "(J)Z", (void*)nativePerfettoDsTraceIterateBegin},
         {"nativePerfettoDsTraceIterateNext", "(J)Z", (void*)nativePerfettoDsTraceIterateNext},
-        {"nativePerfettoDsTraceIterateBreak", "(J)V", (void*)nativePerfettoDsTraceIterateBreak}};
+        {"nativePerfettoDsTraceIterateBreak", "(J)V", (void*)nativePerfettoDsTraceIterateBreak},
+        {"nativeGetPerfettoDsInstanceIndex", "(J)I", (void*)nativeGetPerfettoDsInstanceIndex}};
 
 const JNINativeMethod gMethodsTracingContext[] = {
         /* name, signature, funcPtr */
         {"nativeFlush", "(J[[B)V", (void*)nativeFlush},
         {"nativeGetCustomTls", "(J)Ljava/lang/Object;", (void*)nativeGetCustomTls},
         {"nativeGetIncrementalState", "(J)Ljava/lang/Object;", (void*)nativeGetIncrementalState},
+        {"nativeSetCustomTls", "(JLjava/lang/Object;)V", (void*)nativeSetCustomTls},
+        {"nativeSetIncrementalState", "(JLjava/lang/Object;)V", (void*)nativeSetIncrementalState},
 };
 
 int register_android_tracing_PerfettoDataSource(JNIEnv* env) {
diff --git a/core/jni/android_tracing_PerfettoDataSource.h b/core/jni/android_tracing_PerfettoDataSource.h
index 209de29..fe15184 100644
--- a/core/jni/android_tracing_PerfettoDataSource.h
+++ b/core/jni/android_tracing_PerfettoDataSource.h
@@ -44,16 +44,16 @@
     jobject newInstance(JNIEnv* env, void* ds_config, size_t ds_config_size,
                         PerfettoDsInstanceIndex inst_id);
 
-    jobject createTlsStateGlobalRef(JNIEnv* env, PerfettoDsInstanceIndex inst_id);
-    jobject createIncrementalStateGlobalRef(JNIEnv* env, PerfettoDsInstanceIndex inst_id);
-
     bool TraceIterateBegin();
     bool TraceIterateNext();
     void TraceIterateBreak();
+    PerfettoDsInstanceIndex GetInstanceIndex();
     void WritePackets(JNIEnv* env, jobjectArray packets);
 
     jobject GetCustomTls();
+    void SetCustomTls(jobject);
     jobject GetIncrementalState();
+    void SetIncrementalState(jobject);
 
     void flushAll();
 
diff --git a/core/jni/com_android_internal_content_FileSystemUtils.cpp b/core/jni/com_android_internal_content_FileSystemUtils.cpp
index 4bd2d72..01920de 100644
--- a/core/jni/com_android_internal_content_FileSystemUtils.cpp
+++ b/core/jni/com_android_internal_content_FileSystemUtils.cpp
@@ -42,7 +42,11 @@
 bool punchHoles(const char *filePath, const uint64_t offset,
                 const std::vector<Elf64_Phdr> &programHeaders) {
     struct stat64 beforePunch;
-    lstat64(filePath, &beforePunch);
+    if (int result = lstat64(filePath, &beforePunch); result != 0) {
+        ALOGE("lstat64 failed for filePath %s, error:%d", filePath, errno);
+        return false;
+    }
+
     uint64_t blockSize = beforePunch.st_blksize;
     IF_ALOGD() {
         ALOGD("Total number of LOAD segments %zu", programHeaders.size());
@@ -152,7 +156,10 @@
 
     IF_ALOGD() {
         struct stat64 afterPunch;
-        lstat64(filePath, &afterPunch);
+        if (int result = lstat64(filePath, &afterPunch); result != 0) {
+            ALOGD("lstat64 failed for filePath %s, error:%d", filePath, errno);
+            return false;
+        }
         ALOGD("Size after punching holes st_blocks: %" PRIu64 ", st_blksize: %ld, st_size: %" PRIu64
               "",
               afterPunch.st_blocks, afterPunch.st_blksize,
@@ -177,7 +184,7 @@
 
     // only consider elf64 for punching holes
     if (ehdr.e_ident[EI_CLASS] != ELFCLASS64) {
-        ALOGE("Provided file is not ELF64");
+        ALOGW("Provided file is not ELF64");
         return false;
     }
 
@@ -215,4 +222,108 @@
     return punchHoles(filePath, offset, programHeaders);
 }
 
+bool punchHolesInZip(const char *filePath, uint64_t offset, uint16_t extraFieldLen) {
+    android::base::unique_fd fd(open(filePath, O_RDWR | O_CLOEXEC));
+    if (!fd.ok()) {
+        ALOGE("Can't open file to punch %s", filePath);
+        return false;
+    }
+
+    struct stat64 beforePunch;
+    if (int result = lstat64(filePath, &beforePunch); result != 0) {
+        ALOGE("lstat64 failed for filePath %s, error:%d", filePath, errno);
+        return false;
+    }
+
+    uint64_t blockSize = beforePunch.st_blksize;
+    IF_ALOGD() {
+        ALOGD("Extra field length: %hu,  Size before punching holes st_blocks: %" PRIu64
+              ", st_blksize: %ld, st_size: %" PRIu64 "",
+              extraFieldLen, beforePunch.st_blocks, beforePunch.st_blksize,
+              static_cast<uint64_t>(beforePunch.st_size));
+    }
+
+    if (extraFieldLen < blockSize) {
+        ALOGD("Skipping punching apk as extra field length is less than block size");
+        return false;
+    }
+
+    // content is preceded by extra field. Zip offset is offset of exact content.
+    // move back by extraFieldLen so that scan can be started at start of extra field.
+    uint64_t extraFieldStart;
+    if (__builtin_sub_overflow(offset, extraFieldLen, &extraFieldStart)) {
+        ALOGE("Overflow occurred when calculating start of extra field");
+        return false;
+    }
+
+    constexpr uint64_t kMaxSize = 64 * 1024;
+    // Use malloc to gracefully handle any oom conditions
+    std::unique_ptr<uint8_t, decltype(&free)> buffer(static_cast<uint8_t *>(malloc(kMaxSize)),
+                                                     &free);
+    if (buffer == nullptr) {
+        ALOGE("Failed to allocate read buffer");
+        return false;
+    }
+
+    // Read the entire extra fields at once and punch file according to zero stretches.
+    if (!ReadFullyAtOffset(fd, buffer.get(), extraFieldLen, extraFieldStart)) {
+        ALOGE("Failed to read extra field content");
+        return false;
+    }
+
+    IF_ALOGD() {
+        ALOGD("Extra field length: %hu content near offset: %s", extraFieldLen,
+              HexString(buffer.get(), extraFieldLen).c_str());
+    }
+
+    uint64_t currentSize = 0;
+    while (currentSize < extraFieldLen) {
+        uint64_t end = currentSize;
+        // find zero ranges
+        while (end < extraFieldLen && *(buffer.get() + end) == 0) {
+            ++end;
+        }
+
+        uint64_t punchLen;
+        if (__builtin_sub_overflow(end, currentSize, &punchLen)) {
+            ALOGW("Overflow occurred when calculating punching length");
+            return false;
+        }
+
+        // Don't punch for every stretch of zero which is found
+        if (punchLen > blockSize) {
+            uint64_t punchOffset;
+            if (__builtin_add_overflow(extraFieldStart, currentSize, &punchOffset)) {
+                ALOGW("Overflow occurred when calculating punch start offset");
+                return false;
+            }
+
+            ALOGD("Punching hole in apk start: %" PRIu64 " len:%" PRIu64 "", punchOffset, punchLen);
+
+            // Punch hole for this entire stretch.
+            int result = fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, punchOffset,
+                                   punchLen);
+            if (result < 0) {
+                ALOGE("fallocate failed to punch hole inside apk, error:%d", errno);
+                return false;
+            }
+        }
+        currentSize = end;
+        ++currentSize;
+    }
+
+    IF_ALOGD() {
+        struct stat64 afterPunch;
+        if (int result = lstat64(filePath, &afterPunch); result != 0) {
+            ALOGD("lstat64 failed for filePath %s, error:%d", filePath, errno);
+            return false;
+        }
+        ALOGD("punchHolesInApk:: Size after punching holes st_blocks: %" PRIu64
+              ", st_blksize: %ld, st_size: %" PRIu64 "",
+              afterPunch.st_blocks, afterPunch.st_blksize,
+              static_cast<uint64_t>(afterPunch.st_size));
+    }
+    return true;
+}
+
 }; // namespace android
diff --git a/core/jni/com_android_internal_content_FileSystemUtils.h b/core/jni/com_android_internal_content_FileSystemUtils.h
index a6b145c..52445e2 100644
--- a/core/jni/com_android_internal_content_FileSystemUtils.h
+++ b/core/jni/com_android_internal_content_FileSystemUtils.h
@@ -28,4 +28,11 @@
  */
 bool punchHolesInElf64(const char* filePath, uint64_t offset);
 
+/*
+ * This function punches holes in zero segments of Apk file which are introduced during the
+ * alignment. Alignment tools add padding inside of extra field in local file header. punch holes in
+ * extra field for zero stretches till the actual file content.
+ */
+bool punchHolesInZip(const char* filePath, uint64_t offset, uint16_t extraFieldLen);
+
 } // namespace android
\ No newline at end of file
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index faa83f8..9b8dab7 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -28,6 +28,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
+#include <sys/statfs.h>
 #include <sys/types.h>
 #include <time.h>
 #include <unistd.h>
@@ -145,8 +146,9 @@
 
     uint16_t method;
     off64_t offset;
-
-    if (!zipFile->getEntryInfo(zipEntry, &method, &uncompLen, nullptr, &offset, &when, &crc)) {
+    uint16_t extraFieldLength;
+    if (!zipFile->getEntryInfo(zipEntry, &method, &uncompLen, nullptr, &offset, &when, &crc,
+                               &extraFieldLength)) {
         ALOGE("Couldn't read zip entry info\n");
         return INSTALL_FAILED_INVALID_APK;
     }
@@ -177,6 +179,12 @@
                   "%" PRIu64 "",
                   fileName, zipFile->getZipFileName(), offset);
         }
+
+        // if extra field for this zip file is present with some length, possibility is that it is
+        // padding added for zip alignment. Punch holes there too.
+        if (!punchHolesInZip(zipFile->getZipFileName(), offset, extraFieldLength)) {
+            ALOGW("Failed to punch apk : %s at extra field", zipFile->getZipFileName());
+        }
 #endif // ENABLE_PUNCH_HOLES
 
         return INSTALL_SUCCEEDED;
@@ -279,6 +287,25 @@
         return INSTALL_FAILED_CONTAINER_ERROR;
     }
 
+#ifdef ENABLE_PUNCH_HOLES
+    // punch extracted elf files as well. This will fail where compression is on (like f2fs) but it
+    // will be useful for ext4 based systems
+    struct statfs64 fsInfo;
+    int result = statfs64(localFileName, &fsInfo);
+    if (result < 0) {
+        ALOGW("Failed to stat file :%s", localFileName);
+    }
+
+    if (result == 0 && fsInfo.f_type == EXT4_SUPER_MAGIC) {
+        ALOGD("Punching extracted elf file %s on fs:%" PRIu64 "", fileName,
+              static_cast<uint64_t>(fsInfo.f_type));
+        if (!punchHolesInElf64(localFileName, 0)) {
+            ALOGW("Failed to punch extracted elf file :%s from apk : %s", fileName,
+                  zipFile->getZipFileName());
+        }
+    }
+#endif // ENABLE_PUNCH_HOLES
+
     ALOGV("Successfully moved %s to %s\n", localTmpFileName, localFileName);
 
     return INSTALL_SUCCEEDED;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ab714ad..f55f3c7 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5890,7 +5890,7 @@
     <!-- Allows an application to subscribe to notifications about the nearby devices' presence
          status change base on the UUIDs.
          <p>Not for use by third-party applications.</p>
-         @FlaggedApi("android.companion.flags.device_presence")
+         @FlaggedApi("android.companion.device_presence")
     -->
     <permission android:name="android.permission.REQUEST_OBSERVE_DEVICE_UUID_PRESENCE"
                 android:protectionLevel="signature|privileged" />
@@ -8182,6 +8182,15 @@
     <permission android:name="android.permission.SCREEN_TIMEOUT_OVERRIDE"
                 android:protectionLevel="signature" />
 
+    <!-- @SystemApi
+        @FlaggedApi("android.security.fsverity_api")
+        Allows app to setup fs-verity through FileIntegrityManager.
+        <p>Protection level: signature|privileged
+        @hide
+    -->
+    <permission android:name="android.permission.SETUP_FSVERITY"
+                android:protectionLevel="signature|privileged"/>
+
     <!-- Attribution for Geofencing service. -->
     <attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
     <!-- Attribution for Country Detector. -->
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index be1c939..6f06d80 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -80,6 +80,7 @@
         android:layout_height="match_parent"
         android:layout_alignParentStart="true"
         android:importantForAccessibility="no"
+        android:focusable="false"
         />
 
     <include layout="@layout/notification_expand_button"
diff --git a/core/res/res/layout/notification_template_material_base.xml b/core/res/res/layout/notification_template_material_base.xml
index 710a70a..64227d8 100644
--- a/core/res/res/layout/notification_template_material_base.xml
+++ b/core/res/res/layout/notification_template_material_base.xml
@@ -55,6 +55,7 @@
         android:layout_height="match_parent"
         android:layout_gravity="start"
         android:importantForAccessibility="no"
+        android:focusable="false"
         />
 
     <LinearLayout
diff --git a/core/res/res/layout/notification_template_material_media.xml b/core/res/res/layout/notification_template_material_media.xml
index df32d30..8a94c48 100644
--- a/core/res/res/layout/notification_template_material_media.xml
+++ b/core/res/res/layout/notification_template_material_media.xml
@@ -54,6 +54,7 @@
         android:layout_height="match_parent"
         android:layout_gravity="start"
         android:importantForAccessibility="no"
+        android:focusable="false"
         />
 
     <LinearLayout
diff --git a/core/res/res/layout/notification_template_material_messaging.xml b/core/res/res/layout/notification_template_material_messaging.xml
index 3e82bd1..a83d923 100644
--- a/core/res/res/layout/notification_template_material_messaging.xml
+++ b/core/res/res/layout/notification_template_material_messaging.xml
@@ -67,6 +67,7 @@
                 android:layout_height="match_parent"
                 android:layout_gravity="start"
                 android:importantForAccessibility="no"
+                android:focusable="false"
                 />
 
             <!--
diff --git a/core/tests/coretests/src/android/view/ViewFrameRateTest.java b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
index 28343f1..0bf9a4c 100644
--- a/core/tests/coretests/src/android/view/ViewFrameRateTest.java
+++ b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
@@ -203,7 +203,9 @@
         mActivityRule.runOnUiThread(() -> {
             mMovingView.setFrameContentVelocity(1_000_000_000f);
             mMovingView.invalidate();
-            runAfterDraw(() -> assertEquals(140f, mViewRoot.getLastPreferredFrameRate(), 0f));
+            runAfterDraw(() -> {
+                assertEquals(140f, mViewRoot.getLastPreferredFrameRate(), 0f);
+            });
         });
         waitForAfterDraw();
     }
@@ -411,6 +413,26 @@
         waitForAfterDraw();
     }
 
+    @Test
+    @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
+            FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY,
+            FLAG_TOOLKIT_FRAME_RATE_VELOCITY_MAPPING_READ_ONLY
+    })
+    public void frameRateAndCategory() throws Throwable {
+        waitForFrameRateCategoryToSettle();
+        mActivityRule.runOnUiThread(() -> {
+            mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_LOW);
+            mMovingView.setFrameContentVelocity(1f);
+            mMovingView.invalidate();
+            runAfterDraw(() -> {
+                assertEquals(FRAME_RATE_CATEGORY_LOW,
+                        mViewRoot.getLastPreferredFrameRateCategory());
+                assertEquals(60f, mViewRoot.getLastPreferredFrameRate());
+            });
+        });
+        waitForAfterDraw();
+    }
+
     private void runAfterDraw(@NonNull Runnable runnable) {
         Handler handler = new Handler(Looper.getMainLooper());
         mAfterDrawLatch = new CountDownLatch(1);
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index e0282a4..80fef6c 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -52,6 +52,7 @@
 import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -806,37 +807,50 @@
             assertEquals(0, mViewRootImpl.getPreferredFrameRate(), 0.1);
             assertEquals(mViewRootImpl.getFrameRateCompatibility(),
                     FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
-            assertEquals(false, mViewRootImpl.isFrameRateConflicted());
+            assertFalse(mViewRootImpl.isFrameRateConflicted());
             mViewRootImpl.votePreferredFrameRate(24, FRAME_RATE_COMPATIBILITY_GTE);
-            assertEquals(24, mViewRootImpl.getPreferredFrameRate(), 0.1);
-            assertEquals(FRAME_RATE_COMPATIBILITY_GTE,
-                    mViewRootImpl.getFrameRateCompatibility());
-            assertEquals(false, mViewRootImpl.isFrameRateConflicted());
+            if (toolkitFrameRateVelocityMappingReadOnly()) {
+                assertEquals(24, mViewRootImpl.getPreferredFrameRate(), 0.1);
+                assertEquals(FRAME_RATE_COMPATIBILITY_GTE,
+                        mViewRootImpl.getFrameRateCompatibility());
+                assertFalse(mViewRootImpl.isFrameRateConflicted());
+            } else {
+                assertEquals(FRAME_RATE_CATEGORY_HIGH,
+                        mViewRootImpl.getPreferredFrameRateCategory());
+            }
             mViewRootImpl.votePreferredFrameRate(30, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
             assertEquals(30, mViewRootImpl.getPreferredFrameRate(), 0.1);
             // If there is a conflict, then set compatibility to
             // FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
             assertEquals(FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
                     mViewRootImpl.getFrameRateCompatibility());
-            // Should be true since there is a conflict between 24 and 30.
-            assertTrue(mViewRootImpl.isFrameRateConflicted());
+            if (toolkitFrameRateVelocityMappingReadOnly()) {
+                // Should be true since there is a conflict between 24 and 30.
+                assertTrue(mViewRootImpl.isFrameRateConflicted());
+            }
+
             mView.invalidate();
         });
         sInstrumentation.waitForIdleSync();
 
         sInstrumentation.runOnMainSync(() -> {
-            assertEquals(false, mViewRootImpl.isFrameRateConflicted());
+            assertFalse(mViewRootImpl.isFrameRateConflicted());
             mViewRootImpl.votePreferredFrameRate(60, FRAME_RATE_COMPATIBILITY_GTE);
-            assertEquals(60, mViewRootImpl.getPreferredFrameRate(), 0.1);
-            assertEquals(FRAME_RATE_COMPATIBILITY_GTE,
-                    mViewRootImpl.getFrameRateCompatibility());
-            assertEquals(mViewRootImpl.isFrameRateConflicted(), false);
+            if (toolkitFrameRateVelocityMappingReadOnly()) {
+                assertEquals(60, mViewRootImpl.getPreferredFrameRate(), 0.1);
+                assertEquals(FRAME_RATE_COMPATIBILITY_GTE,
+                        mViewRootImpl.getFrameRateCompatibility());
+            } else {
+                assertEquals(FRAME_RATE_CATEGORY_HIGH,
+                        mViewRootImpl.getPreferredFrameRateCategory());
+            }
+            assertFalse(mViewRootImpl.isFrameRateConflicted());
             mViewRootImpl.votePreferredFrameRate(120, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
             assertEquals(120, mViewRootImpl.getPreferredFrameRate(), 0.1);
             assertEquals(FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
                     mViewRootImpl.getFrameRateCompatibility());
             // Should be false since 60 is a divisor of 120.
-            assertEquals(false, mViewRootImpl.isFrameRateConflicted());
+            assertFalse(mViewRootImpl.isFrameRateConflicted());
             mViewRootImpl.votePreferredFrameRate(60, FRAME_RATE_COMPATIBILITY_GTE);
             assertEquals(120, mViewRootImpl.getPreferredFrameRate(), 0.1);
             // compatibility should be remained the same (FRAME_RATE_COMPATIBILITY_FIXED_SOURCE)
@@ -844,8 +858,7 @@
             assertEquals(FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
                     mViewRootImpl.getFrameRateCompatibility());
             // Should be false since 60 is a divisor of 120.
-            assertEquals(false, mViewRootImpl.isFrameRateConflicted());
-
+            assertFalse(mViewRootImpl.isFrameRateConflicted());
         });
     }
 
@@ -872,7 +885,7 @@
         // reset the frame rate category counts
         for (int i = 0; i < 5; i++) {
             sInstrumentation.runOnMainSync(() -> {
-                mView.setRequestedFrameRate(mView.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+                mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
                 mView.invalidate();
             });
             sInstrumentation.waitForIdleSync();
@@ -881,21 +894,21 @@
         waitForFrameRateCategoryToSettle(mView);
 
         sInstrumentation.runOnMainSync(() -> {
-            mView.setRequestedFrameRate(mView.REQUESTED_FRAME_RATE_CATEGORY_LOW);
+            mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_LOW);
             mView.invalidate();
             runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_LOW,
                     mViewRootImpl.getLastPreferredFrameRateCategory()));
         });
         waitForAfterDraw();
         sInstrumentation.runOnMainSync(() -> {
-            mView.setRequestedFrameRate(mView.REQUESTED_FRAME_RATE_CATEGORY_NORMAL);
+            mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NORMAL);
             mView.invalidate();
             runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_NORMAL,
                     mViewRootImpl.getLastPreferredFrameRateCategory()));
         });
         waitForAfterDraw();
         sInstrumentation.runOnMainSync(() -> {
-            mView.setRequestedFrameRate(mView.REQUESTED_FRAME_RATE_CATEGORY_HIGH);
+            mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_HIGH);
             mView.invalidate();
             runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_HIGH,
                     mViewRootImpl.getLastPreferredFrameRateCategory()));
@@ -932,24 +945,20 @@
             assertEquals(0, mViewRootImpl.getPreferredFrameRate(), 0.1);
             mView.setFrameContentVelocity(100);
             mView.invalidate();
-            if (toolkitFrameRateVelocityMappingReadOnly()) {
-                runAfterDraw(() -> assertTrue(mViewRootImpl.getLastPreferredFrameRate() > 0));
-            } else {
-                runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_HIGH,
-                        mViewRootImpl.getLastPreferredFrameRateCategory()));
-            }
+            runAfterDraw(() -> {
+                if (toolkitFrameRateVelocityMappingReadOnly()) {
+                    assertEquals(FRAME_RATE_CATEGORY_LOW,
+                            mViewRootImpl.getLastPreferredFrameRateCategory());
+                    assertTrue(mViewRootImpl.getLastPreferredFrameRate() >= 60f);
+                } else {
+                    assertEquals(FRAME_RATE_CATEGORY_HIGH,
+                            mViewRootImpl.getLastPreferredFrameRateCategory());
+                    assertEquals(0, mViewRootImpl.getLastPreferredFrameRate(), 0.1);
+                }
+            });
         });
         waitForAfterDraw();
         sInstrumentation.waitForIdleSync();
-        if (toolkitFrameRateVelocityMappingReadOnly()) {
-            assertEquals(FRAME_RATE_CATEGORY_HIGH,
-                    mViewRootImpl.getLastPreferredFrameRateCategory());
-            assertTrue(mViewRootImpl.getLastPreferredFrameRate() >= 60f);
-        } else {
-            assertEquals(FRAME_RATE_CATEGORY_HIGH,
-                    mViewRootImpl.getLastPreferredFrameRateCategory());
-            assertEquals(0, mViewRootImpl.getLastPreferredFrameRate(), 0.1);
-        }
     }
 
     /**
@@ -1002,9 +1011,9 @@
 
         ViewRootImpl viewRootImpl = mView.getViewRootImpl();
         final WindowManager.LayoutParams attrs = viewRootImpl.mWindowAttributes;
-        assertEquals(true, attrs.getFrameRateBoostOnTouchEnabled());
-        assertEquals(viewRootImpl.getFrameRateBoostOnTouchEnabled(),
-                attrs.getFrameRateBoostOnTouchEnabled());
+        assertTrue(attrs.getFrameRateBoostOnTouchEnabled());
+        assertEquals(attrs.getFrameRateBoostOnTouchEnabled(),
+                viewRootImpl.getFrameRateBoostOnTouchEnabled());
 
         sInstrumentation.runOnMainSync(() -> {
             attrs.setFrameRateBoostOnTouchEnabled(false);
@@ -1014,9 +1023,9 @@
 
         sInstrumentation.runOnMainSync(() -> {
             final WindowManager.LayoutParams newAttrs = viewRootImpl.mWindowAttributes;
-            assertEquals(false, newAttrs.getFrameRateBoostOnTouchEnabled());
-            assertEquals(viewRootImpl.getFrameRateBoostOnTouchEnabled(),
-                    newAttrs.getFrameRateBoostOnTouchEnabled());
+            assertFalse(newAttrs.getFrameRateBoostOnTouchEnabled());
+            assertEquals(newAttrs.getFrameRateBoostOnTouchEnabled(),
+                    viewRootImpl.getFrameRateBoostOnTouchEnabled());
         });
     }
 
@@ -1040,24 +1049,24 @@
             assertEquals(0, viewRootImpl.getPreferredFrameRate(), 0.1);
             assertEquals(FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
                     viewRootImpl.getFrameRateCompatibility());
-            assertEquals(false, viewRootImpl.isFrameRateConflicted());
+            assertFalse(viewRootImpl.isFrameRateConflicted());
             viewRootImpl.votePreferredFrameRate(24, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
             assertEquals(24, viewRootImpl.getPreferredFrameRate(), 0.1);
             assertEquals(FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
                     viewRootImpl.getFrameRateCompatibility());
-            assertEquals(false, viewRootImpl.isFrameRateConflicted());
+            assertFalse(viewRootImpl.isFrameRateConflicted());
             mView.invalidate();
             assertEquals(24, viewRootImpl.getPreferredFrameRate(), 0.1);
             assertEquals(FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
                     viewRootImpl.getFrameRateCompatibility());
-            assertEquals(false, viewRootImpl.isFrameRateConflicted());
+            assertFalse(viewRootImpl.isFrameRateConflicted());
         });
 
         Thread.sleep(delay);
         assertEquals(0, viewRootImpl.getPreferredFrameRate(), 0.1);
         assertEquals(FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
                 viewRootImpl.getFrameRateCompatibility());
-        assertEquals(false, viewRootImpl.isFrameRateConflicted());
+        assertFalse(viewRootImpl.isFrameRateConflicted());
     }
 
     /**
@@ -1093,14 +1102,14 @@
         // reset the frame rate category counts
         for (int i = 0; i < 5; i++) {
             sInstrumentation.runOnMainSync(() -> {
-                mView.setRequestedFrameRate(mView.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+                mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
                 mView.invalidate();
             });
             sInstrumentation.waitForIdleSync();
         }
 
         sInstrumentation.runOnMainSync(() -> {
-            mView.setRequestedFrameRate(mView.REQUESTED_FRAME_RATE_CATEGORY_LOW);
+            mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_LOW);
             mView.invalidate();
             runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_LOW,
                     mViewRootImpl.getLastPreferredFrameRateCategory()));
@@ -1153,7 +1162,7 @@
         // reset the frame rate category counts
         for (int i = 0; i < 5; i++) {
             sInstrumentation.runOnMainSync(() -> {
-                mView.setRequestedFrameRate(mView.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+                mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
                 mView.invalidate();
             });
             sInstrumentation.waitForIdleSync();
@@ -1162,15 +1171,17 @@
         // In transition from frequent update to infrequent update
         Thread.sleep(delay);
         sInstrumentation.runOnMainSync(() -> {
-            mView.setRequestedFrameRate(mView.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+            mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
             mView.invalidate();
-            runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_NO_PREFERENCE,
-                    mViewRootImpl.getLastPreferredFrameRateCategory()));
+            runAfterDraw(() -> {
+                assertEquals(FRAME_RATE_CATEGORY_NO_PREFERENCE,
+                        mViewRootImpl.getLastPreferredFrameRateCategory());
+            });
         });
         waitForAfterDraw();
         Thread.sleep(delay);
         sInstrumentation.runOnMainSync(() -> {
-            mView.setRequestedFrameRate(mView.REQUESTED_FRAME_RATE_CATEGORY_DEFAULT);
+            mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_DEFAULT);
             mView.invalidate();
             runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_NO_PREFERENCE,
                     mViewRootImpl.getLastPreferredFrameRateCategory()));
@@ -1180,7 +1191,7 @@
         // Infrequent update
         Thread.sleep(delay);
         sInstrumentation.runOnMainSync(() -> {
-            mView.setRequestedFrameRate(mView.REQUESTED_FRAME_RATE_CATEGORY_DEFAULT);
+            mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_DEFAULT);
             mView.invalidate();
             runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_NORMAL,
                     mViewRootImpl.getLastPreferredFrameRateCategory()));
@@ -1201,9 +1212,9 @@
 
         ViewRootImpl viewRoot = mView.getViewRootImpl();
         final WindowManager.LayoutParams attrs = viewRoot.mWindowAttributes;
-        assertEquals(attrs.isFrameRatePowerSavingsBalanced(), true);
-        assertEquals(viewRoot.isFrameRatePowerSavingsBalanced(),
-                attrs.isFrameRatePowerSavingsBalanced());
+        assertTrue(attrs.isFrameRatePowerSavingsBalanced());
+        assertEquals(attrs.isFrameRatePowerSavingsBalanced(),
+                viewRoot.isFrameRatePowerSavingsBalanced());
 
         sInstrumentation.runOnMainSync(() -> {
             attrs.setFrameRatePowerSavingsBalanced(false);
@@ -1213,9 +1224,9 @@
 
         sInstrumentation.runOnMainSync(() -> {
             final WindowManager.LayoutParams newAttrs = viewRoot.mWindowAttributes;
-            assertEquals(false, newAttrs.isFrameRatePowerSavingsBalanced());
-            assertEquals(viewRoot.isFrameRatePowerSavingsBalanced(),
-                    newAttrs.isFrameRatePowerSavingsBalanced());
+            assertFalse(newAttrs.isFrameRatePowerSavingsBalanced());
+            assertEquals(newAttrs.isFrameRatePowerSavingsBalanced(),
+                    viewRoot.isFrameRatePowerSavingsBalanced());
         });
     }
 
@@ -1266,7 +1277,7 @@
         for (int i = 0; i < 5; i++) {
             Thread.sleep(delay);
             sInstrumentation.runOnMainSync(() -> {
-                mView.setRequestedFrameRate(mView.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+                mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
                 mView.invalidate();
             });
             sInstrumentation.waitForIdleSync();
@@ -1274,7 +1285,7 @@
 
         Thread.sleep(delay);
         sInstrumentation.runOnMainSync(() -> {
-            mView.setRequestedFrameRate(mView.REQUESTED_FRAME_RATE_CATEGORY_DEFAULT);
+            mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_DEFAULT);
             mView.invalidate();
             runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_NORMAL,
                     mViewRootImpl.getLastPreferredFrameRateCategory()));
@@ -1410,7 +1421,7 @@
             mViewRootImpl.dispatchInputEvent(event);
         });
         sInstrumentation.waitForIdleSync();
-        assertEquals(mKeyReceived, shouldReceiveKey);
+        assertEquals(shouldReceiveKey, mKeyReceived);
     }
 
     private void attachViewToWindow(View view) {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 0f04321..46a3e7f 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -861,9 +861,9 @@
         // container update.
         updateDivider(wct, taskContainer);
 
-        // If the last direct activity of the host task is dismissed and the overlay container is
-        // the only taskFragment, the overlay container should also be dismissed.
-        dismissOverlayContainerIfNeeded(wct, taskContainer);
+        // If the last direct activity of the host task is dismissed and there's an always-on-top
+        // overlay container in the task, the overlay container should also be dismissed.
+        dismissAlwaysOnTopOverlayIfNeeded(wct, taskContainer);
 
         if (!shouldUpdateContainer) {
             return;
@@ -1990,7 +1990,7 @@
             @NonNull TaskFragmentContainer container) {
         final TaskContainer taskContainer = container.getTaskContainer();
 
-        if (dismissOverlayContainerIfNeeded(wct, taskContainer)) {
+        if (dismissAlwaysOnTopOverlayIfNeeded(wct, taskContainer)) {
             return;
         }
 
@@ -2014,22 +2014,27 @@
         }
     }
 
-    /** Dismisses the overlay container in the {@code taskContainer} if needed. */
+    /**
+     * Dismisses {@link TaskFragmentContainer#isAlwaysOnTopOverlay()} in the {@code taskContainer}
+     * if needed.
+     */
     @GuardedBy("mLock")
-    private boolean dismissOverlayContainerIfNeeded(@NonNull WindowContainerTransaction wct,
-            @NonNull TaskContainer taskContainer) {
-        final TaskFragmentContainer overlayContainer = taskContainer.getOverlayContainer();
-        if (overlayContainer == null) {
+    private boolean dismissAlwaysOnTopOverlayIfNeeded(@NonNull WindowContainerTransaction wct,
+                                                      @NonNull TaskContainer taskContainer) {
+        // Dismiss always-on-top overlay container if it's the only container in the task and
+        // there's no direct activity in the parent task.
+        final List<TaskFragmentContainer> containers = taskContainer.getTaskFragmentContainers();
+        if (containers.size() != 1 || taskContainer.hasDirectActivity()) {
             return false;
         }
-        // Dismiss the overlay container if it's the only container in the task and there's no
-        // direct activity in the parent task.
-        if (taskContainer.getTaskFragmentContainers().size() == 1
-                && !taskContainer.hasDirectActivity()) {
-            mPresenter.cleanupContainer(wct, overlayContainer, false /* shouldFinishDependant */);
-            return true;
+
+        final TaskFragmentContainer container = containers.getLast();
+        if (!container.isAlwaysOnTopOverlay()) {
+            return false;
         }
-        return false;
+
+        mPresenter.cleanupContainer(wct, container, false /* shouldFinishDependant */);
+        return true;
     }
 
     /**
@@ -2620,25 +2625,42 @@
     /**
      * Gets all overlay containers from all tasks in this process, or an empty list if there's
      * no overlay container.
-     * <p>
-     * Note that we only support one overlay container for each task, but an app could have multiple
-     * tasks.
      */
     @VisibleForTesting
     @GuardedBy("mLock")
     @NonNull
-    List<TaskFragmentContainer> getAllOverlayTaskFragmentContainers() {
+    List<TaskFragmentContainer> getAllNonFinishingOverlayContainers() {
         final List<TaskFragmentContainer> overlayContainers = new ArrayList<>();
         for (int i = 0; i < mTaskContainers.size(); i++) {
             final TaskContainer taskContainer = mTaskContainers.valueAt(i);
-            final TaskFragmentContainer overlayContainer = taskContainer.getOverlayContainer();
-            if (overlayContainer != null) {
-                overlayContainers.add(overlayContainer);
-            }
+            final List<TaskFragmentContainer> overlayContainersPerTask = taskContainer
+                    .getTaskFragmentContainers()
+                    .stream()
+                    .filter(c -> c.isOverlay() && !c.isFinished())
+                    .toList();
+            overlayContainers.addAll(overlayContainersPerTask);
         }
         return overlayContainers;
     }
 
+    /**
+     * Creates an overlay container or updates a visible overlay container if its
+     * {@link TaskFragmentContainer#getTaskId()}, {@link TaskFragmentContainer#getOverlayTag()}
+     * and {@link TaskFragmentContainer#getAssociatedActivityToken()} matches.
+     * <p>
+     * This method will also dismiss any existing overlay container if:
+     * <ul>
+     *   <li>it's visible but not meet the criteria to update overlay</li>
+     *   <li>{@link TaskFragmentContainer#getOverlayTag()} matches but not meet the criteria to
+     *   update overlay</li>
+     * </ul>
+     *
+     * @param wct the {@link WindowContainerTransaction}
+     * @param options the {@link ActivityOptions} to launch the overlay
+     * @param intent the intent of activity to launch
+     * @param launchActivity the activity to launch the overlay container
+     * @return the overlay container
+     */
     @VisibleForTesting
     // Suppress GuardedBy warning because lint ask to mark this method as
     // @GuardedBy(container.mController.mLock), which is mLock itself
@@ -2649,7 +2671,7 @@
             @NonNull WindowContainerTransaction wct, @NonNull Bundle options,
             @NonNull Intent intent, @NonNull Activity launchActivity) {
         final List<TaskFragmentContainer> overlayContainers =
-                getAllOverlayTaskFragmentContainers();
+                getAllNonFinishingOverlayContainers();
         final String overlayTag = Objects.requireNonNull(options.getString(KEY_OVERLAY_TAG));
         final boolean associateLaunchingActivity = options
                 .getBoolean(KEY_OVERLAY_ASSOCIATE_WITH_LAUNCHING_ACTIVITY, true);
@@ -2673,62 +2695,86 @@
         final int taskId = getTaskId(launchActivity);
         if (!overlayContainers.isEmpty()) {
             for (final TaskFragmentContainer overlayContainer : overlayContainers) {
-                if (!overlayTag.equals(overlayContainer.getOverlayTag())
-                        && taskId == overlayContainer.getTaskId()) {
-                    // If there's an overlay container with different tag shown in the same
-                    // task, dismiss the existing overlay container.
-                    mPresenter.cleanupContainer(wct, overlayContainer,
-                            false /* shouldFinishDependant */);
-                }
-                if (overlayTag.equals(overlayContainer.getOverlayTag())
-                        && taskId != overlayContainer.getTaskId()) {
-                    Log.w(TAG, "The overlay container with tag:"
-                            + overlayContainer.getOverlayTag() + " is dismissed because"
-                            + " there's an existing overlay container with the same tag but"
-                            + " different task ID:" + overlayContainer.getTaskId() + ". "
-                            + "The new associated activity is " + launchActivity);
+                final boolean isTopNonFinishingOverlay = overlayContainer.equals(
+                        overlayContainer.getTaskContainer().getTopNonFinishingTaskFragmentContainer(
+                                true /* includePin */, true /* includeOverlay */));
+                if (taskId != overlayContainer.getTaskId()) {
                     // If there's an overlay container with same tag in a different task,
                     // dismiss the overlay container since the tag must be unique per process.
-                    mPresenter.cleanupContainer(wct, overlayContainer,
-                            false /* shouldFinishDependant */);
-                }
-                if (overlayTag.equals(overlayContainer.getOverlayTag())
-                        && taskId == overlayContainer.getTaskId()) {
-                    if (associateLaunchingActivity && !launchActivity.getActivityToken()
-                            .equals(overlayContainer.getAssociatedActivityToken())) {
+                    if (overlayTag.equals(overlayContainer.getOverlayTag())) {
                         Log.w(TAG, "The overlay container with tag:"
                                 + overlayContainer.getOverlayTag() + " is dismissed because"
                                 + " there's an existing overlay container with the same tag but"
-                                + " different associated launching activity. The new associated"
-                                + " activity is " + launchActivity);
-                        // The associated activity must be the same, or it will be dismissed.
+                                + " different task ID:" + overlayContainer.getTaskId() + ". "
+                                + "The new associated activity is " + launchActivity);
                         mPresenter.cleanupContainer(wct, overlayContainer,
                                 false /* shouldFinishDependant */);
-                    } else if (!associateLaunchingActivity
-                            && overlayContainer.isAssociatedWithActivity()) {
+                    }
+                    continue;
+                }
+                if (!overlayTag.equals(overlayContainer.getOverlayTag())) {
+                    // If there's an overlay container with different tag on top in the same
+                    // task, dismiss the existing overlay container.
+                    if (isTopNonFinishingOverlay) {
+                        mPresenter.cleanupContainer(wct, overlayContainer,
+                                false /* shouldFinishDependant */);
+                    }
+                    continue;
+                }
+                // The overlay container has the same tag and task ID with the new launching
+                // overlay container.
+                if (!isTopNonFinishingOverlay) {
+                    // Dismiss the invisible overlay container regardless of activity
+                    // association if it collides the tag of new launched overlay container .
+                    Log.w(TAG, "The invisible overlay container with tag:"
+                            + overlayContainer.getOverlayTag() + " is dismissed because"
+                            + " there's a launching overlay container with the same tag."
+                            + " The new associated activity is " + launchActivity);
+                    mPresenter.cleanupContainer(wct, overlayContainer,
+                            false /* shouldFinishDependant */);
+                    continue;
+                }
+                // Requesting an always-on-top overlay.
+                if (!associateLaunchingActivity) {
+                    if (overlayContainer.isAssociatedWithActivity()) {
+                        // Dismiss the overlay container since it has associated with an activity.
                         Log.w(TAG, "The overlay container with tag:"
                                 + overlayContainer.getOverlayTag() + " is dismissed because"
                                 + " there's an existing overlay container with the same tag but"
                                 + " different associated launching activity. The overlay container"
                                 + " doesn't associate with any activity.");
-                        // Dismiss the overlay container since it has been associated with an
-                        // activity.
                         mPresenter.cleanupContainer(wct, overlayContainer,
                                 false /* shouldFinishDependant */);
+                        continue;
                     } else {
-                        // Just update the overlay container if
-                        // - should associate with an activity and associated activity matches
-                        // - should not associate with an activity and the overlay container
-                        //     don't have an associated activity
+                        // The existing overlay container doesn't associate an activity as well.
+                        // Just update the overlay and return.
+                        // Note that going to this condition means the tag, task ID matches a
+                        // visible always-on-top overlay, and won't dismiss any overlay any more.
                         mPresenter.applyActivityStackAttributes(wct, overlayContainer, attrs,
                                 getMinDimensions(intent));
-                        // We can just return the updated overlay container and don't need to
-                        // check other condition since we only have one OverlayCreateParams, and
-                        // if the tag and task are matched, it's impossible to match another task
-                        // or tag since tags and tasks are all unique.
                         return overlayContainer;
                     }
                 }
+                if (launchActivity.getActivityToken()
+                        != overlayContainer.getAssociatedActivityToken()) {
+                    Log.w(TAG, "The overlay container with tag:"
+                            + overlayContainer.getOverlayTag() + " is dismissed because"
+                            + " there's an existing overlay container with the same tag but"
+                            + " different associated launching activity. The new associated"
+                            + " activity is " + launchActivity);
+                    // The associated activity must be the same, or it will be dismissed.
+                    mPresenter.cleanupContainer(wct, overlayContainer,
+                            false /* shouldFinishDependant */);
+                    continue;
+                }
+                // Reaching here means the launching activity launch an overlay container with the
+                // same task ID, tag, while there's a previously launching visible overlay
+                // container. We'll regard it as updating the existing overlay container.
+                mPresenter.applyActivityStackAttributes(wct, overlayContainer, attrs,
+                        getMinDimensions(intent));
+                return overlayContainer;
+
             }
         }
         // Launch the overlay container to the task with taskId.
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index b56f671..6231ea0 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -467,6 +467,11 @@
             reorderTaskFragmentToFront(wct,
                     pinnedContainer.getSecondaryContainer().getTaskFragmentToken());
         }
+        final TaskFragmentContainer alwaysOnTopOverlayContainer = container.getTaskContainer()
+                .getAlwaysOnTopOverlayContainer();
+        if (alwaysOnTopOverlayContainer != null) {
+            reorderTaskFragmentToFront(wct, alwaysOnTopOverlayContainer.getTaskFragmentToken());
+        }
     }
 
     @Override
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
index 3dd96c4..fdf0910 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
@@ -70,9 +70,11 @@
     @Nullable
     private SplitPinContainer mSplitPinContainer;
 
-    /** The overlay container in this Task. */
+    /**
+     * The {@link TaskFragmentContainer#isAlwaysOnTopOverlay()} in the Task.
+     */
     @Nullable
-    private TaskFragmentContainer mOverlayContainer;
+    private TaskFragmentContainer mAlwaysOnTopOverlayContainer;
 
     @NonNull
     private final Configuration mConfiguration;
@@ -316,10 +318,12 @@
         return null;
     }
 
-    /** Returns the overlay container in the task, or {@code null} if it doesn't exist. */
+    /**
+     * Returns the always-on-top overlay container in the task, or {@code null} if it doesn't exist.
+     */
     @Nullable
-    TaskFragmentContainer getOverlayContainer() {
-        return mOverlayContainer;
+    TaskFragmentContainer getAlwaysOnTopOverlayContainer() {
+        return mAlwaysOnTopOverlayContainer;
     }
 
     int indexOf(@NonNull TaskFragmentContainer child) {
@@ -531,7 +535,21 @@
         updateSplitPinContainerIfNecessary();
         // Update overlay container after split pin container since the overlay should be on top of
         // pin container.
-        updateOverlayContainerIfNecessary();
+        updateAlwaysOnTopOverlayIfNecessary();
+    }
+
+    private void updateAlwaysOnTopOverlayIfNecessary() {
+        final List<TaskFragmentContainer> alwaysOnTopOverlays = mContainers
+                .stream().filter(TaskFragmentContainer::isAlwaysOnTopOverlay).toList();
+        if (alwaysOnTopOverlays.size() > 1) {
+            throw new IllegalStateException("There must be at most one always-on-top overlay "
+                    + "container per Task");
+        }
+        mAlwaysOnTopOverlayContainer = alwaysOnTopOverlays.isEmpty()
+                ? null : alwaysOnTopOverlays.getFirst();
+        if (mAlwaysOnTopOverlayContainer != null) {
+            moveContainerToLastIfNecessary(mAlwaysOnTopOverlayContainer);
+        }
     }
 
     private void updateSplitPinContainerIfNecessary() {
@@ -559,18 +577,6 @@
         }
     }
 
-    private void updateOverlayContainerIfNecessary() {
-        final List<TaskFragmentContainer> overlayContainers = mContainers.stream()
-                .filter(TaskFragmentContainer::isOverlay).toList();
-        if (overlayContainers.size() > 1) {
-            throw new IllegalStateException("There must be at most one overlay container per Task");
-        }
-        mOverlayContainer = overlayContainers.isEmpty() ? null : overlayContainers.get(0);
-        if (mOverlayContainer != null) {
-            moveContainerToLastIfNecessary(mOverlayContainer);
-        }
-    }
-
     /** Moves the {@code container} to the last to align taskFragments' z-order. */
     private void moveContainerToLastIfNecessary(@NonNull TaskFragmentContainer container) {
         final int index = mContainers.indexOf(container);
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index 5dbb016..094ebcb 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -1023,6 +1023,14 @@
         return mAssociatedActivityToken != null;
     }
 
+    /**
+     * Returns {@code true} if the overlay container should be always on top, which should be
+     * a non-fill-parent overlay without activity association.
+     */
+    boolean isAlwaysOnTopOverlay() {
+        return isOverlay() && !isAssociatedWithActivity();
+    }
+
     @Override
     public String toString() {
         return toString(true /* includeContainersToFinishOnExit */);
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
index dcdbe59..b1b1984 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
@@ -178,37 +178,59 @@
     }
 
     @Test
-    public void testGetOverlayContainers() {
-        assertThat(mSplitController.getAllOverlayTaskFragmentContainers()).isEmpty();
+    public void testGetAllNonFinishingOverlayContainers() {
+        assertThat(mSplitController.getAllNonFinishingOverlayContainers()).isEmpty();
 
         final TaskFragmentContainer overlayContainer1 =
                 createTestOverlayContainer(TASK_ID, "test1");
 
-        assertThat(mSplitController.getAllOverlayTaskFragmentContainers())
+        assertThat(mSplitController.getAllNonFinishingOverlayContainers())
                 .containsExactly(overlayContainer1);
 
-        assertThrows(
-                "The exception must throw if there are two overlay containers in the same task.",
-                IllegalStateException.class,
-                () -> createTestOverlayContainer(TASK_ID, "test2"));
+        final TaskFragmentContainer overlayContainer2 =
+                createTestOverlayContainer(TASK_ID, "test2");
+
+        assertThat(mSplitController.getAllNonFinishingOverlayContainers())
+                .containsExactly(overlayContainer1, overlayContainer2);
 
         final TaskFragmentContainer overlayContainer3 =
                 createTestOverlayContainer(TASK_ID + 1, "test3");
 
-        assertThat(mSplitController.getAllOverlayTaskFragmentContainers())
-                .containsExactly(overlayContainer1, overlayContainer3);
+        assertThat(mSplitController.getAllNonFinishingOverlayContainers())
+                .containsExactly(overlayContainer1, overlayContainer2, overlayContainer3);
+
+        final TaskFragmentContainer finishingOverlayContainer =
+                createTestOverlayContainer(TASK_ID, "test4");
+        spyOn(finishingOverlayContainer);
+        doReturn(true).when(finishingOverlayContainer).isFinished();
+
+        assertThat(mSplitController.getAllNonFinishingOverlayContainers())
+                .containsExactly(overlayContainer1, overlayContainer2, overlayContainer3);
     }
 
     @Test
-    public void testCreateOrUpdateOverlayTaskFragmentIfNeeded_anotherTagInTask_dismissOverlay() {
+    public void testCreateOrUpdateOverlayTaskFragmentIfNeeded_anotherTagInTask() {
+        createExistingOverlayContainers(false /* visible */);
+        createMockTaskFragmentContainer(mActivity);
+
+        final TaskFragmentContainer overlayContainer =
+                createOrUpdateOverlayTaskFragmentIfNeeded("test3");
+
+        assertWithMessage("overlayContainer1 is still there since it's not visible.")
+                .that(mSplitController.getAllNonFinishingOverlayContainers())
+                .containsExactly(mOverlayContainer1, mOverlayContainer2, overlayContainer);
+    }
+
+    @Test
+    public void testCreateOrUpdateOverlay_visibleOverlaySameTagInTask_dismissOverlay() {
         createExistingOverlayContainers();
 
         final TaskFragmentContainer overlayContainer =
                 createOrUpdateOverlayTaskFragmentIfNeeded("test3");
 
-        assertWithMessage("overlayContainer1 must be dismissed since the new overlay container"
-                + " is launched to the same task")
-                .that(mSplitController.getAllOverlayTaskFragmentContainers())
+        assertWithMessage("overlayContainer1 must be dismissed since it's visible"
+                + " in the same task.")
+                .that(mSplitController.getAllNonFinishingOverlayContainers())
                 .containsExactly(mOverlayContainer2, overlayContainer);
     }
 
@@ -223,7 +245,7 @@
         assertWithMessage("overlayContainer1 must be dismissed since the new overlay container"
                 + " is launched with the same tag as an existing overlay container in a different "
                 + "task")
-                .that(mSplitController.getAllOverlayTaskFragmentContainers())
+                .that(mSplitController.getAllNonFinishingOverlayContainers())
                 .containsExactly(mOverlayContainer2, overlayContainer);
     }
 
@@ -240,7 +262,7 @@
 
         assertWithMessage("overlayContainer1 must be updated since the new overlay container"
                 + " is launched with the same tag and task")
-                .that(mSplitController.getAllOverlayTaskFragmentContainers())
+                .that(mSplitController.getAllNonFinishingOverlayContainers())
                 .containsExactly(mOverlayContainer1, mOverlayContainer2);
 
         assertThat(overlayContainer).isEqualTo(mOverlayContainer1);
@@ -260,11 +282,26 @@
 
         assertWithMessage("overlayContainer1 must be dismissed since the new overlay container"
                 + " is associated with different launching activity")
-                .that(mSplitController.getAllOverlayTaskFragmentContainers())
+                .that(mSplitController.getAllNonFinishingOverlayContainers())
                 .containsExactly(mOverlayContainer2, overlayContainer);
     }
 
     @Test
+    public void testCreateOrUpdateOverlayTaskFragmentIfNeeded_dismissOverlay() {
+        createExistingOverlayContainers(false /* visible */);
+        createMockTaskFragmentContainer(mActivity);
+
+        final TaskFragmentContainer overlayContainer =
+                createOrUpdateOverlayTaskFragmentIfNeeded("test2");
+
+        // OverlayContainer2 is dismissed since new container is launched with the
+        // same tag in different task.
+        assertWithMessage("overlayContainer1 must be dismissed")
+                .that(mSplitController.getAllNonFinishingOverlayContainers())
+                .containsExactly(mOverlayContainer1, overlayContainer);
+    }
+
+    @Test
     public void testCreateOrUpdateOverlayTaskFragmentIfNeeded_dismissMultipleOverlays() {
         createExistingOverlayContainers();
 
@@ -275,15 +312,19 @@
         // different tag. OverlayContainer2 is dismissed since new container is launched with the
         // same tag in different task.
         assertWithMessage("overlayContainer1 and overlayContainer2 must be dismissed")
-                .that(mSplitController.getAllOverlayTaskFragmentContainers())
+                .that(mSplitController.getAllNonFinishingOverlayContainers())
                 .containsExactly(overlayContainer);
     }
 
     private void createExistingOverlayContainers() {
-        mOverlayContainer1 = createTestOverlayContainer(TASK_ID, "test1");
-        mOverlayContainer2 = createTestOverlayContainer(TASK_ID + 1, "test2");
+        createExistingOverlayContainers(true /* visible */);
+    }
+
+    private void createExistingOverlayContainers(boolean visible) {
+        mOverlayContainer1 = createTestOverlayContainer(TASK_ID, "test1", visible);
+        mOverlayContainer2 = createTestOverlayContainer(TASK_ID + 1, "test2", visible);
         List<TaskFragmentContainer> overlayContainers = mSplitController
-                .getAllOverlayTaskFragmentContainers();
+                .getAllNonFinishingOverlayContainers();
         assertThat(overlayContainers).containsExactly(mOverlayContainer1, mOverlayContainer2);
     }
 
@@ -314,7 +355,7 @@
                 createOrUpdateOverlayTaskFragmentIfNeeded("test");
         setupTaskFragmentInfo(overlayContainer, mActivity, true /* isVisible */);
 
-        assertThat(mSplitController.getAllOverlayTaskFragmentContainers())
+        assertThat(mSplitController.getAllNonFinishingOverlayContainers())
                 .containsExactly(overlayContainer);
         assertThat(overlayContainer.getTaskId()).isEqualTo(TASK_ID);
         assertThat(overlayContainer.areLastRequestedBoundsEqual(bounds)).isTrue();
@@ -322,14 +363,16 @@
     }
 
     @Test
-    public void testGetTopNonFishingTaskFragmentContainerWithOverlay() {
-        final TaskFragmentContainer overlayContainer =
-                createTestOverlayContainer(TASK_ID, "test1");
-
-        // Add a SplitPinContainer, the overlay should be on top
+    public void testGetTopNonFishingTaskFragmentContainerWithoutAssociatedOverlay() {
         final Activity primaryActivity = createMockActivity();
         final Activity secondaryActivity = createMockActivity();
-
+        final Rect bounds = new Rect(0, 0, 100, 100);
+        mSplitController.setActivityStackAttributesCalculator(params ->
+                new ActivityStackAttributes.Builder().setRelativeBounds(bounds).build());
+        final TaskFragmentContainer overlayContainer =
+                createTestOverlayContainer(TASK_ID, "test1", true /* isVisible */,
+                        false /* shouldAssociateWithActivity */);
+        overlayContainer.setIsolatedNavigationEnabled(true);
         final TaskFragmentContainer primaryContainer =
                 createMockTaskFragmentContainer(primaryActivity);
         final TaskFragmentContainer secondaryContainer =
@@ -371,10 +414,10 @@
 
     @Test
     public void testGetTopNonFinishingActivityWithOverlay() {
-        TaskFragmentContainer overlayContainer = createTestOverlayContainer(TASK_ID, "test1");
-
         final Activity activity = createMockActivity();
         final TaskFragmentContainer container = createMockTaskFragmentContainer(activity);
+        final TaskFragmentContainer overlayContainer = createTestOverlayContainer(TASK_ID,
+                "test1");
         final TaskContainer task = container.getTaskContainer();
 
         assertThat(task.getTopNonFinishingActivity(true /* includeOverlay */))
@@ -391,8 +434,9 @@
     }
 
     @Test
-    public void testUpdateOverlayContainer_dismissOverlayIfNeeded() {
-        TaskFragmentContainer overlayContainer = createTestOverlayContainer(TASK_ID, "test");
+    public void testUpdateOverlayContainer_dismissNonAssociatedOverlayIfNeeded() {
+        TaskFragmentContainer overlayContainer = createTestOverlayContainer(TASK_ID, "test",
+                true /* isVisible */, false /* associatedLaunchingActivity */);
 
         mSplitController.updateOverlayContainer(mTransaction, overlayContainer);
 
@@ -461,11 +505,10 @@
 
     @Test
     public void testOnTaskFragmentParentInfoChanged_positionOnlyChange_earlyReturn() {
-        final TaskFragmentContainer overlayContainer = createTestOverlayContainer(TASK_ID, "test");
+        final TaskFragmentContainer overlayContainer = createTestOverlayContainer(TASK_ID, "test",
+                true /* isVisible */, false /* associatedLaunchingActivity */);
         final TaskContainer taskContainer = overlayContainer.getTaskContainer();
 
-        assertThat(taskContainer.getOverlayContainer()).isEqualTo(overlayContainer);
-
         spyOn(taskContainer);
         final TaskContainer.TaskProperties taskProperties = taskContainer.getTaskProperties();
         final TaskFragmentParentInfo parentInfo = new TaskFragmentParentInfo(
@@ -481,16 +524,15 @@
 
         assertWithMessage("The overlay container must still be dismissed even if "
                 + "#updateContainer is not called")
-                .that(taskContainer.getOverlayContainer()).isNull();
+                .that(taskContainer.getTaskFragmentContainers()).isEmpty();
     }
 
     @Test
-    public void testOnTaskFragmentParentInfoChanged_invisibleTask_callDismissOverlayContainer() {
-        final TaskFragmentContainer overlayContainer = createTestOverlayContainer(TASK_ID, "test");
+    public void testOnTaskFragmentParentInfoChanged_invisibleTask_callDismissNonAssocOverlay() {
+        final TaskFragmentContainer overlayContainer = createTestOverlayContainer(TASK_ID, "test",
+                true /* isVisible */, false /* associatedLaunchingActivity */);
         final TaskContainer taskContainer = overlayContainer.getTaskContainer();
 
-        assertThat(taskContainer.getOverlayContainer()).isEqualTo(overlayContainer);
-
         spyOn(taskContainer);
         final TaskContainer.TaskProperties taskProperties = taskContainer.getTaskProperties();
         final TaskFragmentParentInfo parentInfo = new TaskFragmentParentInfo(
@@ -505,7 +547,7 @@
 
         assertWithMessage("The overlay container must still be dismissed even if "
                 + "#updateContainer is not called")
-                .that(taskContainer.getOverlayContainer()).isNull();
+                .that(taskContainer.getTaskFragmentContainers()).isEmpty();
     }
 
     @Test
@@ -529,8 +571,7 @@
 
     @Test
     public void testApplyActivityStackAttributesForOverlayContainerAssociatedWithActivity() {
-        final TaskFragmentContainer container = createTestOverlayContainer(TASK_ID,
-                TEST_TAG, true /* associatedWithLaunchingActivity */);
+        final TaskFragmentContainer container = createTestOverlayContainer(TASK_ID, TEST_TAG);
         final IBinder token = container.getTaskFragmentToken();
         final ActivityStackAttributes attributes = new ActivityStackAttributes.Builder()
                 .setRelativeBounds(new Rect(0, 0, 200, 200))
@@ -555,7 +596,7 @@
     @Test
     public void testApplyActivityStackAttributesForOverlayContainerWithoutAssociatedActivity() {
         final TaskFragmentContainer container = createTestOverlayContainer(TASK_ID, TEST_TAG,
-                false /* associatedWithLaunchingActivity */);
+                true, /* isVisible */ false /* associatedWithLaunchingActivity */);
         final IBinder token = container.getTaskFragmentToken();
         final ActivityStackAttributes attributes = new ActivityStackAttributes.Builder()
                 .setRelativeBounds(new Rect(0, 0, 200, 200))
@@ -610,8 +651,7 @@
         mSplitPresenter.applyActivityStackAttributes(mTransaction, container, attributes,
                 new Size(relativeBounds.width() + 1, relativeBounds.height()));
 
-        verify(mSplitPresenter).resizeTaskFragmentIfRegistered(mTransaction, container,
-                new Rect());
+        verify(mSplitPresenter).resizeTaskFragmentIfRegistered(mTransaction, container, new Rect());
         verify(mSplitPresenter).updateTaskFragmentWindowingModeIfRegistered(mTransaction, container,
                 WINDOWING_MODE_UNDEFINED);
         verify(mSplitPresenter).updateAnimationParams(mTransaction, token,
@@ -635,14 +675,14 @@
                 mActivity.getActivityToken());
 
         verify(mSplitPresenter, never()).cleanupContainer(any(), any(), anyBoolean());
-        assertThat(mSplitController.getAllOverlayTaskFragmentContainers())
+        assertThat(mSplitController.getAllNonFinishingOverlayContainers())
                 .contains(overlayWithoutAssociation);
 
         TaskFragmentContainer overlayWithAssociation =
                 createOrUpdateOverlayTaskFragmentIfNeeded("test");
         overlayWithAssociation.setInfo(mTransaction, createMockTaskFragmentInfo(
                 overlayWithAssociation, mActivity, true /* isVisible */));
-        assertThat(mSplitController.getAllOverlayTaskFragmentContainers())
+        assertThat(mSplitController.getAllNonFinishingOverlayContainers())
                 .contains(overlayWithAssociation);
         clearInvocations(mSplitPresenter);
 
@@ -655,7 +695,7 @@
 
         verify(mSplitPresenter).cleanupContainer(mTransaction, overlayWithAssociation, false);
 
-        assertThat(mSplitController.getAllOverlayTaskFragmentContainers())
+        assertThat(mSplitController.getAllNonFinishingOverlayContainers())
                 .doesNotContain(overlayWithAssociation);
     }
 
@@ -692,7 +732,14 @@
 
     @NonNull
     private TaskFragmentContainer createTestOverlayContainer(int taskId, @NonNull String tag) {
-        return createTestOverlayContainer(taskId, tag,
+        return createTestOverlayContainer(taskId, tag, false /* isVisible */,
+                true /* associateLaunchingActivity */);
+    }
+
+    @NonNull
+    private TaskFragmentContainer createTestOverlayContainer(int taskId, @NonNull String tag,
+            boolean isVisible) {
+        return createTestOverlayContainer(taskId, tag, isVisible,
                 true /* associateLaunchingActivity */);
     }
 
@@ -700,13 +747,13 @@
     //  once we have use cases.
     @NonNull
     private TaskFragmentContainer createTestOverlayContainer(int taskId, @NonNull String tag,
-                boolean associateLaunchingActivity) {
+                boolean isVisible, boolean associateLaunchingActivity) {
         Activity activity = createMockActivity();
         TaskFragmentContainer overlayContainer = mSplitController.newContainer(
                 null /* pendingAppearedActivity */, mIntent, activity, taskId,
                 null /* pairedPrimaryContainer */, tag, Bundle.EMPTY,
                 associateLaunchingActivity);
-        setupTaskFragmentInfo(overlayContainer, activity, false /* isVisible */);
+        setupTaskFragmentInfo(overlayContainer, activity, isVisible);
         return overlayContainer;
     }
 
diff --git a/libs/WindowManager/Shell/AndroidManifest.xml b/libs/WindowManager/Shell/AndroidManifest.xml
index 36d3313..7a98683 100644
--- a/libs/WindowManager/Shell/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/AndroidManifest.xml
@@ -23,4 +23,12 @@
     <uses-permission android:name="android.permission.ROTATE_SURFACE_FLINGER" />
     <uses-permission android:name="android.permission.WAKEUP_SURFACE_FLINGER" />
     <uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
+
+    <application>
+        <activity
+            android:name=".desktopmode.DesktopWallpaperActivity"
+            android:excludeFromRecents="true"
+            android:launchMode="singleInstance"
+            android:theme="@style/DesktopWallpaperTheme" />
+    </application>
 </manifest>
diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml
index 08c2a02..13c0e66 100644
--- a/libs/WindowManager/Shell/res/values/styles.xml
+++ b/libs/WindowManager/Shell/res/values/styles.xml
@@ -23,6 +23,14 @@
         <item name="android:windowAnimationStyle">@style/Animation.ForcedResizable</item>
     </style>
 
+    <!-- Theme used for the activity that shows below the desktop mode windows to show wallpaper -->
+    <style name="DesktopWallpaperTheme" parent="@android:style/Theme.Wallpaper.NoTitleBar">
+        <item name="android:statusBarColor">@android:color/transparent</item>
+        <item name="android:navigationBarColor">@android:color/transparent</item>
+        <item name="android:windowDrawsSystemBarBackgrounds">true</item>
+        <item name="android:windowAnimationStyle">@null</item>
+    </style>
+
     <style name="Animation.ForcedResizable" parent="@android:style/Animation">
         <item name="android:activityOpenEnterAnimation">@anim/forced_resizable_enter</item>
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index ff00a7b..1408ead 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -59,6 +59,7 @@
 import com.android.wm.shell.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
 import com.android.wm.shell.desktopmode.DesktopTasksController;
+import com.android.wm.shell.desktopmode.DesktopTasksTransitionObserver;
 import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler;
 import com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler;
 import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler;
@@ -569,6 +570,18 @@
 
     @WMSingleton
     @Provides
+    static Optional<DesktopTasksTransitionObserver> provideDesktopTasksTransitionObserver(
+            Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
+            Transitions transitions,
+            ShellInit shellInit
+    ) {
+        return desktopModeTaskRepository.flatMap(repository ->
+                Optional.of(new DesktopTasksTransitionObserver(repository, transitions, shellInit))
+        );
+    }
+
+    @WMSingleton
+    @Provides
     static DesktopModeLoggerTransitionObserver provideDesktopModeLoggerTransitionObserver(
             ShellInit shellInit,
             Transitions transitions,
@@ -623,7 +636,8 @@
     @Provides
     static Object provideIndependentShellComponentsToCreate(
             DragAndDropController dragAndDropController,
-            DefaultMixedHandler defaultMixedHandler) {
+            DefaultMixedHandler defaultMixedHandler,
+            Optional<DesktopTasksTransitionObserver> desktopTasksTransitionObserverOptional) {
         return new Object();
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt
index e1e41ee..f1a475a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt
@@ -36,7 +36,14 @@
                     true
                 }
             }
-
+            "moveToNextDisplay" -> {
+                if (!runMoveToNextDisplay(args, pw)) {
+                    pw.println("Task not found. Please enter a valid taskId.")
+                    false
+                } else {
+                    true
+                }
+            }
             else -> {
                 pw.println("Invalid command: ${args[0]}")
                 false
@@ -61,8 +68,28 @@
         return controller.moveToDesktop(taskId, WindowContainerTransaction())
     }
 
+    private fun runMoveToNextDisplay(args: Array<String>, pw: PrintWriter): Boolean {
+        if (args.size < 2) {
+            // First argument is the action name.
+            pw.println("Error: task id should be provided as arguments")
+            return false
+        }
+
+        val taskId = try {
+            args[1].toInt()
+        } catch (e: NumberFormatException) {
+            pw.println("Error: task id should be an integer")
+            return false
+        }
+
+        controller.moveToNextDisplay(taskId)
+        return true
+    }
+
     override fun printShellCommandHelp(pw: PrintWriter, prefix: String) {
         pw.println("$prefix moveToDesktop <taskId> ")
         pw.println("$prefix  Move a task with given id to desktop mode.")
+        pw.println("$prefix moveToNextDisplay <taskId> ")
+        pw.println("$prefix  Move a task with given id to next display.")
     }
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
index 120d681..50cea01 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
@@ -22,6 +22,7 @@
 import android.util.ArraySet
 import android.util.SparseArray
 import android.view.Display.INVALID_DISPLAY
+import android.window.WindowContainerToken
 import androidx.core.util.forEach
 import androidx.core.util.keyIterator
 import androidx.core.util.valueIterator
@@ -49,6 +50,8 @@
         var stashed: Boolean = false
     )
 
+    // Token of the current wallpaper activity, used to remove it when the last task is removed
+    var wallpaperActivityToken: WindowContainerToken? = null
     // Tasks currently in freeform mode, ordered from top to bottom (top is at index 0).
     private val freeformTasksInZOrder = mutableListOf<Int>()
     private val activeTasksListeners = ArraySet<ActiveTasksListener>()
@@ -200,6 +203,15 @@
     }
 
     /**
+     *  Check if a task with the given [taskId] is the only active task on its display
+     */
+    fun isOnlyActiveTask(taskId: Int): Boolean {
+        return displayData.valueIterator().asSequence().any { data ->
+            data.activeTasks.singleOrNull() == taskId
+        }
+    }
+
+    /**
      * Get a set of the active tasks for given [displayId]
      */
     fun getActiveTasks(displayId: Int): ArraySet<Int> {
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 ec2e670..068661a 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
@@ -40,6 +40,7 @@
 import android.view.WindowManager.TRANSIT_CHANGE
 import android.view.WindowManager.TRANSIT_NONE
 import android.view.WindowManager.TRANSIT_OPEN
+import android.view.WindowManager.TRANSIT_TO_BACK
 import android.view.WindowManager.TRANSIT_TO_FRONT
 import android.window.RemoteTransition
 import android.window.TransitionInfo
@@ -381,7 +382,6 @@
         )
         val wct = WindowContainerTransaction()
         exitSplitIfApplicable(wct, taskInfo)
-        moveHomeTaskToFront(wct)
         bringDesktopAppsToFront(taskInfo.displayId, wct)
         addMoveToDesktopChanges(wct, taskInfo)
         wct.setBounds(taskInfo.token, freeformBounds)
@@ -401,6 +401,22 @@
         shellTaskOrganizer.applyTransaction(wct)
     }
 
+    /**
+     * Perform clean up of the desktop wallpaper activity if the closed window task is
+     * the last active task.
+     *
+     * @param wct transaction to modify if the last active task is closed
+     * @param taskId task id of the window that's being closed
+     */
+    fun onDesktopWindowClose(
+        wct: WindowContainerTransaction,
+        taskId: Int
+    ) {
+        if (desktopModeTaskRepository.isOnlyActiveTask(taskId)) {
+            removeWallpaperActivity(wct)
+        }
+    }
+
     /** Move a task with given `taskId` to fullscreen */
     fun moveToFullscreen(taskId: Int) {
         shellTaskOrganizer.getRunningTaskInfo(taskId)?.let { task ->
@@ -676,9 +692,15 @@
         KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: bringDesktopAppsToFront")
         val activeTasks = desktopModeTaskRepository.getActiveTasks(displayId)
 
-        // First move home to front and then other tasks on top of it
-        moveHomeTaskToFront(wct)
+        if (Flags.enableDesktopWindowingWallpaperActivity()) {
+            // Add translucent wallpaper activity to show the wallpaper underneath
+            addWallpaperActivity(wct)
+        } else {
+            // Move home to front
+            moveHomeTaskToFront(wct)
+        }
 
+        // Then move other tasks on top of it
         val allTasksInZOrder = desktopModeTaskRepository.getFreeformTasksInZOrder()
         activeTasks
             // Sort descending as the top task is at index 0. It should be ordered to top last
@@ -694,6 +716,26 @@
             ?.let { homeTask -> wct.reorder(homeTask.getToken(), true /* onTop */) }
     }
 
+    private fun addWallpaperActivity(wct: WindowContainerTransaction) {
+        KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: addWallpaper")
+        val intent = Intent(context, DesktopWallpaperActivity::class.java)
+        val options = ActivityOptions.makeBasic().apply {
+            isPendingIntentBackgroundActivityLaunchAllowedByPermission = true
+            pendingIntentBackgroundActivityStartMode =
+                ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
+        }
+        val pendingIntent = PendingIntent.getActivity(context, /* requestCode = */ 0, intent,
+            PendingIntent.FLAG_IMMUTABLE)
+        wct.sendPendingIntent(pendingIntent, intent, options.toBundle())
+    }
+
+    private fun removeWallpaperActivity(wct: WindowContainerTransaction) {
+        desktopModeTaskRepository.wallpaperActivityToken?.let { token ->
+            KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: removeWallpaper")
+            wct.removeTask(token)
+        }
+    }
+
     fun releaseVisualIndicator() {
         val t = SurfaceControl.Transaction()
         visualIndicator?.releaseVisualIndicator(t)
@@ -741,6 +783,9 @@
                     reason = "recents animation is running"
                     false
                 }
+                // Handle back navigation for the last window if wallpaper available
+                shouldRemoveWallpaper(request) ->
+                    true
                 // Only handle open or to front transitions
                 request.type != TRANSIT_OPEN && request.type != TRANSIT_TO_FRONT -> {
                     reason = "transition type not handled (${request.type})"
@@ -777,6 +822,7 @@
 
         val result = triggerTask?.let { task ->
             when {
+                request.type == TRANSIT_TO_BACK -> handleBackNavigation(task)
                 // If display has tasks stashed, handle as stashed launch
                 task.isStashed -> handleStashedTaskLaunch(task)
                 // Check if the task has a top transparent activity
@@ -824,6 +870,14 @@
         return Flags.enableDesktopWindowingModalsPolicy() && isSingleTopActivityTranslucent(task)
     }
 
+    private fun shouldRemoveWallpaper(request: TransitionRequestInfo): Boolean {
+        return Flags.enableDesktopWindowingWallpaperActivity() &&
+                request.type == TRANSIT_TO_BACK &&
+                request.triggerTask?.let { task ->
+                    desktopModeTaskRepository.isOnlyActiveTask(task.taskId)
+                } ?: false
+    }
+
     private fun handleFreeformTaskLaunch(task: RunningTaskInfo): WindowContainerTransaction? {
         KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: handleFreeformTaskLaunch")
         val activeTasks = desktopModeTaskRepository.getActiveTasks(task.displayId)
@@ -881,12 +935,26 @@
         }
     }
 
+    /** Handle back navigation by removing wallpaper activity if it's the last active task */
+    private fun handleBackNavigation(task: RunningTaskInfo): WindowContainerTransaction? {
+        if (desktopModeTaskRepository.isOnlyActiveTask(task.taskId) &&
+            desktopModeTaskRepository.wallpaperActivityToken != null) {
+            // Remove wallpaper activity when the last active task is removed
+            return WindowContainerTransaction().also { wct ->
+                removeWallpaperActivity(wct)
+            }
+        } else {
+            return null
+        }
+    }
+
     private fun addMoveToDesktopChanges(
         wct: WindowContainerTransaction,
         taskInfo: RunningTaskInfo
     ) {
-        val displayWindowingMode = taskInfo.configuration.windowConfiguration.displayWindowingMode
-        val targetWindowingMode = if (displayWindowingMode == WINDOWING_MODE_FREEFORM) {
+        val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(taskInfo.displayId)!!
+        val tdaWindowingMode = tdaInfo.configuration.windowConfiguration.windowingMode
+        val targetWindowingMode = if (tdaWindowingMode == WINDOWING_MODE_FREEFORM) {
             // Display windowing is freeform, set to undefined and inherit it
             WINDOWING_MODE_UNDEFINED
         } else {
@@ -903,8 +971,9 @@
         wct: WindowContainerTransaction,
         taskInfo: RunningTaskInfo
     ) {
-        val displayWindowingMode = taskInfo.configuration.windowConfiguration.displayWindowingMode
-        val targetWindowingMode = if (displayWindowingMode == WINDOWING_MODE_FULLSCREEN) {
+        val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(taskInfo.displayId)!!
+        val tdaWindowingMode = tdaInfo.configuration.windowConfiguration.windowingMode
+        val targetWindowingMode = if (tdaWindowingMode == WINDOWING_MODE_FULLSCREEN) {
             // Display windowing is fullscreen, set to undefined and inherit it
             WINDOWING_MODE_UNDEFINED
         } else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
new file mode 100644
index 0000000..20df264
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
@@ -0,0 +1,92 @@
+/*
+ * 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.wm.shell.desktopmode
+
+import android.os.IBinder
+import android.view.SurfaceControl
+import android.view.WindowManager
+import android.window.TransitionInfo
+import com.android.window.flags.Flags.enableDesktopWindowingWallpaperActivity
+import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
+import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.transition.Transitions
+import com.android.wm.shell.util.KtProtoLog
+
+/**
+ * A [Transitions.TransitionObserver] that observes shell transitions and updates
+ * the [DesktopModeTaskRepository] state TODO: b/332682201
+ * This observes transitions related to desktop mode
+ * and other transitions that originate both within and outside shell.
+ */
+class DesktopTasksTransitionObserver(
+    private val desktopModeTaskRepository: DesktopModeTaskRepository,
+    private val transitions: Transitions,
+    shellInit: ShellInit
+) : Transitions.TransitionObserver {
+
+    init {
+        if (Transitions.ENABLE_SHELL_TRANSITIONS && DesktopModeStatus.isEnabled()) {
+            shellInit.addInitCallback(::onInit, this)
+        }
+    }
+
+    fun onInit() {
+        KtProtoLog.d(WM_SHELL_DESKTOP_MODE, "DesktopTasksTransitionObserver: onInit")
+        transitions.registerObserver(this)
+    }
+
+    override fun onTransitionReady(
+        transition: IBinder,
+        info: TransitionInfo,
+        startTransaction: SurfaceControl.Transaction,
+        finishTransaction: SurfaceControl.Transaction
+    ) {
+        // TODO: b/332682201 Update repository state
+        updateWallpaperToken(info)
+    }
+
+    override fun onTransitionStarting(transition: IBinder) {
+        // TODO: b/332682201 Update repository state
+    }
+
+    override fun onTransitionMerged(merged: IBinder, playing: IBinder) {
+        // TODO: b/332682201 Update repository state
+    }
+
+    override fun onTransitionFinished(transition: IBinder, aborted: Boolean) {
+        // TODO: b/332682201 Update repository state
+    }
+
+    private fun updateWallpaperToken(info: TransitionInfo) {
+        if (!enableDesktopWindowingWallpaperActivity()) {
+            return
+        }
+        info.changes.forEach { change ->
+            change.taskInfo?.let { taskInfo ->
+                if (DesktopWallpaperActivity.isWallpaperTask(taskInfo)) {
+                    when (change.mode) {
+                        WindowManager.TRANSIT_OPEN ->
+                            desktopModeTaskRepository.wallpaperActivityToken = taskInfo.token
+                        WindowManager.TRANSIT_CLOSE ->
+                            desktopModeTaskRepository.wallpaperActivityToken = null
+                        else -> {}
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopWallpaperActivity.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopWallpaperActivity.kt
new file mode 100644
index 0000000..c4a4474
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopWallpaperActivity.kt
@@ -0,0 +1,57 @@
+/*
+ * 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.wm.shell.desktopmode
+
+import android.app.Activity
+import android.app.ActivityManager
+import android.content.ComponentName
+import android.os.Bundle
+import android.view.WindowManager
+import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
+import com.android.wm.shell.util.KtProtoLog
+
+/**
+ * A transparent activity used in the desktop mode to show the wallpaper under the freeform windows.
+ * This activity will be running in `FULLSCREEN` windowing mode, which ensures it hides Launcher.
+ * When entering desktop, we would ensure that it's added behind desktop apps and removed when
+ * leaving the desktop mode.
+ *
+ * Note! This activity should NOT interact directly with any other code in the Shell without calling
+ * onto the shell main thread. Activities are always started on the main thread.
+ */
+class DesktopWallpaperActivity : Activity() {
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        KtProtoLog.d(WM_SHELL_DESKTOP_MODE, "DesktopWallpaperActivity: onCreate")
+        super.onCreate(savedInstanceState)
+        window.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)
+    }
+
+    companion object {
+        private const val SYSTEM_UI_PACKAGE_NAME = "com.android.systemui"
+        private val wallpaperActivityComponent =
+            ComponentName(SYSTEM_UI_PACKAGE_NAME, DesktopWallpaperActivity::class.java.name)
+
+        @JvmStatic
+        fun isWallpaperTask(taskInfo: ActivityManager.RunningTaskInfo) =
+            taskInfo.baseIntent.component?.let(::isWallpaperComponent) ?: false
+
+        @JvmStatic
+        fun isWallpaperComponent(component: ComponentName) =
+            component == wallpaperActivityComponent
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
index c9185ae..b1a1e59 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
@@ -20,6 +20,7 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
 
+import static com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP;
 import static com.android.wm.shell.transition.Transitions.TransitionObserver;
 
 import android.annotation.NonNull;
@@ -60,7 +61,8 @@
             @NonNull SurfaceControl.Transaction finishTransaction) {
         for (TransitionInfo.Change change : info.getChanges()) {
             final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
-            if (taskInfo == null
+            if (info.getType() == TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP
+                    || taskInfo == null
                     || taskInfo.displayId != DEFAULT_DISPLAY
                     || taskInfo.taskId == -1
                     || !taskInfo.isRunning) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index 32f271b..87dc391 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -19,14 +19,19 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.content.pm.PackageManager.FEATURE_PC;
+import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS;
 import static android.view.WindowManager.TRANSIT_CHANGE;
 
 import android.app.ActivityManager.RunningTaskInfo;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.graphics.Rect;
 import android.os.Handler;
+import android.provider.Settings;
 import android.util.SparseArray;
 import android.view.Choreographer;
+import android.view.Display;
 import android.view.MotionEvent;
 import android.view.SurfaceControl;
 import android.view.View;
@@ -163,10 +168,33 @@
     }
 
     private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) {
-        return taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
-                || (taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD
-                && taskInfo.configuration.windowConfiguration.getDisplayWindowingMode()
-                == WINDOWING_MODE_FREEFORM);
+        if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
+            return true;
+        }
+        if (taskInfo.getActivityType() != ACTIVITY_TYPE_STANDARD) {
+            return false;
+        }
+        final DisplayAreaInfo rootDisplayAreaInfo =
+                mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(taskInfo.displayId);
+        if (rootDisplayAreaInfo != null) {
+            return rootDisplayAreaInfo.configuration.windowConfiguration.getWindowingMode()
+                    == WINDOWING_MODE_FREEFORM;
+        }
+
+        // It is possible that the rootDisplayAreaInfo is null when a task appears soon enough after
+        // a new display shows up, because TDA may appear after task appears in WM shell. Instead of
+        // fixing the synchronization issues, let's use other signals to "guess" the answer. It is
+        // OK in this context because no other captions other than the legacy developer option
+        // freeform and Kingyo/CF PC may use this class. WM shell should have full control over the
+        // condition where captions should show up in all new cases such as desktop mode, for which
+        // we should use different window decor view models. Ultimately Kingyo/CF PC may need to
+        // spin up their own window decor view model when they start to care about multiple
+        // displays.
+        if (isPc()) {
+            return true;
+        }
+        return taskInfo.displayId != Display.DEFAULT_DISPLAY
+                && forcesDesktopModeOnExternalDisplays();
     }
 
     private void createWindowDecoration(
@@ -313,4 +341,17 @@
             return true;
         }
     }
+
+    /**
+     * Returns if this device is a PC.
+     */
+    private boolean isPc() {
+        return mContext.getPackageManager().hasSystemFeature(FEATURE_PC);
+    }
+
+    private boolean forcesDesktopModeOnExternalDisplays() {
+        final ContentResolver resolver = mContext.getContentResolver();
+        return Settings.Global.getInt(resolver,
+                DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS, 0) != 0;
+    }
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 7649a782..777ab9c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -84,6 +84,7 @@
 import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator;
 import com.android.wm.shell.desktopmode.DesktopTasksController;
 import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition;
+import com.android.wm.shell.desktopmode.DesktopWallpaperActivity;
 import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
 import com.android.wm.shell.splitscreen.SplitScreen;
 import com.android.wm.shell.splitscreen.SplitScreen.StageType;
@@ -407,7 +408,9 @@
                     mSplitScreenController.moveTaskToFullscreen(getOtherSplitTask(mTaskId).taskId,
                             SplitScreenController.EXIT_REASON_DESKTOP_MODE);
                 } else {
-                    mTaskOperations.closeTask(mTaskToken);
+                    WindowContainerTransaction wct = new WindowContainerTransaction();
+                    mDesktopTasksController.onDesktopWindowClose(wct, mTaskId);
+                    mTaskOperations.closeTask(mTaskToken, wct);
                 }
             } else if (id == R.id.back_button) {
                 mTaskOperations.injectBackKey();
@@ -1005,6 +1008,7 @@
             return false;
         }
         return DesktopModeStatus.isEnabled()
+                && !DesktopWallpaperActivity.isWallpaperTask(taskInfo)
                 && taskInfo.getWindowingMode() != WINDOWING_MODE_PINNED
                 && taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD
                 && !taskInfo.configuration.windowConfiguration.isAlwaysOnTop()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java
index 1763e4f..53d4e27 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java
@@ -72,7 +72,10 @@
     }
 
     void closeTask(WindowContainerToken taskToken) {
-        WindowContainerTransaction wct = new WindowContainerTransaction();
+        closeTask(taskToken, new WindowContainerTransaction());
+    }
+
+    void closeTask(WindowContainerToken taskToken, WindowContainerTransaction wct) {
         wct.removeTask(taskToken);
         if (Transitions.ENABLE_SHELL_TRANSITIONS) {
             mTransitionStarter.startRemoveTransition(wct);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
index 3672ae3..24f4d92 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
@@ -23,8 +23,10 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 
+import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.app.WindowConfiguration;
+import android.content.Intent;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.IBinder;
@@ -38,6 +40,7 @@
 
     private WindowContainerToken mToken = createMockWCToken();
     private int mParentTaskId = INVALID_TASK_ID;
+    private Intent mBaseIntent = new Intent();
     private @WindowConfiguration.ActivityType int mActivityType = ACTIVITY_TYPE_STANDARD;
     private @WindowConfiguration.WindowingMode int mWindowingMode = WINDOWING_MODE_UNDEFINED;
     private int mDisplayId = Display.DEFAULT_DISPLAY;
@@ -68,6 +71,15 @@
         return this;
     }
 
+    /**
+     * Set {@link ActivityManager.RunningTaskInfo#baseIntent} for the task info, by default
+     * an empty intent is assigned
+     */
+    public TestRunningTaskInfoBuilder setBaseIntent(@NonNull Intent intent) {
+        mBaseIntent = intent;
+        return this;
+    }
+
     public TestRunningTaskInfoBuilder setActivityType(
             @WindowConfiguration.ActivityType int activityType) {
         mActivityType = activityType;
@@ -109,6 +121,7 @@
     public ActivityManager.RunningTaskInfo build() {
         final ActivityManager.RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
         info.taskId = sNextTaskId++;
+        info.baseIntent = mBaseIntent;
         info.parentTaskId = mParentTaskId;
         info.displayId = mDisplayId;
         info.configuration.windowConfiguration.setBounds(mBounds);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
index 0c45d52..b2b54ac 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
@@ -119,6 +119,57 @@
     }
 
     @Test
+    fun isOnlyActiveTask_noActiveTasks() {
+        // Not an active task
+        assertThat(repo.isOnlyActiveTask(1)).isFalse()
+    }
+
+    @Test
+    fun isOnlyActiveTask_singleActiveTask() {
+        repo.addActiveTask(DEFAULT_DISPLAY, 1)
+        // The only active task
+        assertThat(repo.isActiveTask(1)).isTrue()
+        assertThat(repo.isOnlyActiveTask(1)).isTrue()
+        // Not an active task
+        assertThat(repo.isActiveTask(99)).isFalse()
+        assertThat(repo.isOnlyActiveTask(99)).isFalse()
+    }
+
+    @Test
+    fun isOnlyActiveTask_multipleActiveTasks() {
+        repo.addActiveTask(DEFAULT_DISPLAY, 1)
+        repo.addActiveTask(DEFAULT_DISPLAY, 2)
+        // Not the only task
+        assertThat(repo.isActiveTask(1)).isTrue()
+        assertThat(repo.isOnlyActiveTask(1)).isFalse()
+        // Not the only task
+        assertThat(repo.isActiveTask(2)).isTrue()
+        assertThat(repo.isOnlyActiveTask(2)).isFalse()
+        // Not an active task
+        assertThat(repo.isActiveTask(99)).isFalse()
+        assertThat(repo.isOnlyActiveTask(99)).isFalse()
+    }
+
+    @Test
+    fun isOnlyActiveTask_multipleDisplays() {
+        repo.addActiveTask(DEFAULT_DISPLAY, 1)
+        repo.addActiveTask(DEFAULT_DISPLAY, 2)
+        repo.addActiveTask(SECOND_DISPLAY, 3)
+        // Not the only task on DEFAULT_DISPLAY
+        assertThat(repo.isActiveTask(1)).isTrue()
+        assertThat(repo.isOnlyActiveTask(1)).isFalse()
+        // Not the only task on DEFAULT_DISPLAY
+        assertThat(repo.isActiveTask(2)).isTrue()
+        assertThat(repo.isOnlyActiveTask(2)).isFalse()
+        // The only active task on SECOND_DISPLAY
+        assertThat(repo.isActiveTask(3)).isTrue()
+        assertThat(repo.isOnlyActiveTask(3)).isTrue()
+        // Not an active task
+        assertThat(repo.isActiveTask(99)).isFalse()
+        assertThat(repo.isOnlyActiveTask(99)).isFalse()
+    }
+
+    @Test
     fun addListener_notifiesVisibleFreeformTask() {
         repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true)
         val listener = TestVisibilityListener()
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 93a967e..64f6041 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
@@ -23,10 +23,13 @@
 import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
 import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
 import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
+import android.content.Intent
 import android.graphics.Point
 import android.graphics.PointF
 import android.graphics.Rect
 import android.os.Binder
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.SetFlagsRule
 import android.testing.AndroidTestingRunner
 import android.view.Display.DEFAULT_DISPLAY
@@ -34,11 +37,15 @@
 import android.view.WindowManager
 import android.view.WindowManager.TRANSIT_CHANGE
 import android.view.WindowManager.TRANSIT_OPEN
+import android.view.WindowManager.TRANSIT_TO_BACK
 import android.view.WindowManager.TRANSIT_TO_FRONT
 import android.window.DisplayAreaInfo
 import android.window.RemoteTransition
 import android.window.TransitionRequestInfo
+import android.window.WindowContainerToken
 import android.window.WindowContainerTransaction
+import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT
+import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_TASK
 import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER
 import androidx.test.filters.SmallTest
 import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
@@ -161,6 +168,10 @@
                 (i.arguments.first() as Rect).set(STABLE_BOUNDS)
             }
 
+        val tda = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
+        tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
+        whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)).thenReturn(tda)
+
         controller = createController()
         controller.setSplitScreenController(splitScreenController)
 
@@ -219,7 +230,8 @@
     }
 
     @Test
-    fun showDesktopApps_allAppsInvisible_bringsToFront() {
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun showDesktopApps_allAppsInvisible_bringsToFront_desktopWallpaperDisabled() {
         val homeTask = setUpHomeTask()
         val task1 = setUpFreeformTask()
         val task2 = setUpFreeformTask()
@@ -238,7 +250,27 @@
     }
 
     @Test
-    fun showDesktopApps_appsAlreadyVisible_bringsToFront() {
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun showDesktopApps_allAppsInvisible_bringsToFront_desktopWallpaperEnabled() {
+        val task1 = setUpFreeformTask()
+        val task2 = setUpFreeformTask()
+        markTaskHidden(task1)
+        markTaskHidden(task2)
+
+        controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+        val wct =
+            getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+        assertThat(wct.hierarchyOps).hasSize(3)
+        // Expect order to be from bottom: wallpaper intent, task1, task2
+        wct.assertPendingIntentAt(index = 0, desktopWallpaperIntent)
+        wct.assertReorderAt(index = 1, task1)
+        wct.assertReorderAt(index = 2, task2)
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun showDesktopApps_appsAlreadyVisible_bringsToFront_desktopWallpaperDisabled() {
         val homeTask = setUpHomeTask()
         val task1 = setUpFreeformTask()
         val task2 = setUpFreeformTask()
@@ -257,7 +289,27 @@
     }
 
     @Test
-    fun showDesktopApps_someAppsInvisible_reordersAll() {
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun showDesktopApps_appsAlreadyVisible_bringsToFront_desktopWallpaperEnabled() {
+        val task1 = setUpFreeformTask()
+        val task2 = setUpFreeformTask()
+        markTaskVisible(task1)
+        markTaskVisible(task2)
+
+        controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+        val wct =
+            getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+        assertThat(wct.hierarchyOps).hasSize(3)
+        // Expect order to be from bottom: wallpaper intent, task1, task2
+        wct.assertPendingIntentAt(index = 0, desktopWallpaperIntent)
+        wct.assertReorderAt(index = 1, task1)
+        wct.assertReorderAt(index = 2, task2)
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun showDesktopApps_someAppsInvisible_reordersAll_desktopWallpaperDisabled() {
         val homeTask = setUpHomeTask()
         val task1 = setUpFreeformTask()
         val task2 = setUpFreeformTask()
@@ -276,7 +328,27 @@
     }
 
     @Test
-    fun showDesktopApps_noActiveTasks_reorderHomeToTop() {
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun showDesktopApps_someAppsInvisible_reordersAll_desktopWallpaperEnabled() {
+        val task1 = setUpFreeformTask()
+        val task2 = setUpFreeformTask()
+        markTaskHidden(task1)
+        markTaskVisible(task2)
+
+        controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+        val wct =
+            getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+        assertThat(wct.hierarchyOps).hasSize(3)
+        // Expect order to be from bottom: wallpaper intent, task1, task2
+        wct.assertPendingIntentAt(index = 0, desktopWallpaperIntent)
+        wct.assertReorderAt(index = 1, task1)
+        wct.assertReorderAt(index = 2, task2)
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun showDesktopApps_noActiveTasks_reorderHomeToTop_desktopWallpaperDisabled() {
         val homeTask = setUpHomeTask()
 
         controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
@@ -288,7 +360,18 @@
     }
 
     @Test
-    fun showDesktopApps_twoDisplays_bringsToFrontOnlyOneDisplay() {
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun showDesktopApps_noActiveTasks_addDesktopWallpaper_desktopWallpaperEnabled() {
+        controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+        val wct =
+            getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+        wct.assertPendingIntentAt(index = 0, desktopWallpaperIntent)
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun showDesktopApps_twoDisplays_bringsToFrontOnlyOneDisplay_desktopWallpaperDisabled() {
         val homeTaskDefaultDisplay = setUpHomeTask(DEFAULT_DISPLAY)
         val taskDefaultDisplay = setUpFreeformTask(DEFAULT_DISPLAY)
         setUpHomeTask(SECOND_DISPLAY)
@@ -307,6 +390,25 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun showDesktopApps_twoDisplays_bringsToFrontOnlyOneDisplay_desktopWallpaperEnabled() {
+        val taskDefaultDisplay = setUpFreeformTask(DEFAULT_DISPLAY)
+        setUpHomeTask(SECOND_DISPLAY)
+        val taskSecondDisplay = setUpFreeformTask(SECOND_DISPLAY)
+        markTaskHidden(taskDefaultDisplay)
+        markTaskHidden(taskSecondDisplay)
+
+        controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+        val wct =
+            getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+        assertThat(wct.hierarchyOps).hasSize(2)
+        // Expect order to be from bottom: wallpaper intent, task
+        wct.assertPendingIntentAt(index = 0, desktopWallpaperIntent)
+        wct.assertReorderAt(index = 1, taskDefaultDisplay)
+    }
+
+    @Test
     fun getVisibleTaskCount_noTasks_returnsZero() {
         assertThat(controller.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0)
     }
@@ -336,9 +438,10 @@
     }
 
     @Test
-    fun moveToDesktop_displayFullscreen_windowingModeSetToFreeform() {
+    fun moveToDesktop_tdaFullscreen_windowingModeSetToFreeform() {
         val task = setUpFullscreenTask()
-        task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FULLSCREEN
+        val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
+        tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
         controller.moveToDesktop(task)
         val wct = getLatestMoveToDesktopWct()
         assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
@@ -346,9 +449,10 @@
     }
 
     @Test
-    fun moveToDesktop_displayFreeform_windowingModeSetToUndefined() {
+    fun moveToDesktop_tdaFreeform_windowingModeSetToUndefined() {
         val task = setUpFullscreenTask()
-        task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FREEFORM
+        val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
+        tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
         controller.moveToDesktop(task)
         val wct = getLatestMoveToDesktopWct()
         assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
@@ -413,7 +517,8 @@
     }
 
     @Test
-    fun moveToDesktop_otherFreeformTasksBroughtToFront() {
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun moveToDesktop_otherFreeformTasksBroughtToFront_desktopWallpaperDisabled() {
         val homeTask = setUpHomeTask()
         val freeformTask = setUpFreeformTask()
         val fullscreenTask = setUpFullscreenTask()
@@ -431,6 +536,26 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun moveToDesktop_otherFreeformTasksBroughtToFront_desktopWallpaperEnabled() {
+        val freeformTask = setUpFreeformTask()
+        val fullscreenTask = setUpFullscreenTask()
+        markTaskHidden(freeformTask)
+
+        controller.moveToDesktop(fullscreenTask)
+
+        with(getLatestMoveToDesktopWct()) {
+            // Operations should include wallpaper intent, freeform task, fullscreen task
+            assertThat(hierarchyOps).hasSize(3)
+            assertPendingIntentAt(index = 0, desktopWallpaperIntent)
+            assertReorderAt(index = 1, freeformTask)
+            assertReorderAt(index = 2, fullscreenTask)
+            assertThat(changes[fullscreenTask.token.asBinder()]?.windowingMode)
+                .isEqualTo(WINDOWING_MODE_FREEFORM)
+        }
+    }
+
+    @Test
     fun moveToDesktop_onlyFreeformTasksFromCurrentDisplayBroughtToFront() {
         setUpHomeTask(displayId = DEFAULT_DISPLAY)
         val freeformTaskDefault = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
@@ -481,9 +606,10 @@
     }
 
     @Test
-    fun moveToFullscreen_displayFullscreen_windowingModeSetToUndefined() {
+    fun moveToFullscreen_tdaFullscreen_windowingModeSetToUndefined() {
         val task = setUpFreeformTask()
-        task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FULLSCREEN
+        val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
+        tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
         controller.moveToFullscreen(task.taskId)
         val wct = getLatestExitDesktopWct()
         assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
@@ -491,9 +617,10 @@
     }
 
     @Test
-    fun moveToFullscreen_displayFreeform_windowingModeSetToFullscreen() {
+    fun moveToFullscreen_tdaFreeform_windowingModeSetToFullscreen() {
         val task = setUpFreeformTask()
-        task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FREEFORM
+        val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
+        tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
         controller.moveToFullscreen(task.taskId)
         val wct = getLatestExitDesktopWct()
         assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
@@ -595,6 +722,48 @@
     }
 
     @Test
+    fun onDesktopWindowClose_noActiveTasks() {
+        val wct = WindowContainerTransaction()
+        controller.onDesktopWindowClose(wct, 1 /* taskId */)
+        // Doesn't modify transaction
+        assertThat(wct.hierarchyOps).isEmpty()
+    }
+
+    @Test
+    fun onDesktopWindowClose_singleActiveTask_noWallpaperActivityToken() {
+        val task = setUpFreeformTask()
+        val wct = WindowContainerTransaction()
+        controller.onDesktopWindowClose(wct, task.taskId)
+        // Doesn't modify transaction
+        assertThat(wct.hierarchyOps).isEmpty()
+    }
+
+    @Test
+    fun onDesktopWindowClose_singleActiveTask_hasWallpaperActivityToken() {
+        val task = setUpFreeformTask()
+        val wallpaperToken = MockToken().token()
+        desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
+
+        val wct = WindowContainerTransaction()
+        controller.onDesktopWindowClose(wct, task.taskId)
+        // Adds remove wallpaper operation
+        wct.assertRemoveAt(index = 0, wallpaperToken)
+    }
+
+    @Test
+    fun onDesktopWindowClose_multipleActiveTasks() {
+        val task1 = setUpFreeformTask()
+        setUpFreeformTask()
+        val wallpaperToken = MockToken().token()
+        desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
+
+        val wct = WindowContainerTransaction()
+        controller.onDesktopWindowClose(wct, task1.taskId)
+        // Doesn't modify transaction
+        assertThat(wct.hierarchyOps).isEmpty()
+    }
+
+    @Test
     fun handleRequest_fullscreenTask_freeformVisible_returnSwitchToFreeformWCT() {
         assumeTrue(ENABLE_SHELL_TRANSITIONS)
 
@@ -638,6 +807,7 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
     fun handleRequest_fullscreenTask_desktopStashed_returnWCTWithAllAppsBroughtToFront() {
         assumeTrue(ENABLE_SHELL_TRANSITIONS)
         whenever(DesktopModeStatus.isStashingEnabled()).thenReturn(true)
@@ -684,7 +854,7 @@
                 createTransition(freeformTask2, type = TRANSIT_TO_FRONT)
             )
         assertThat(result?.changes?.get(freeformTask2.token.asBinder())?.windowingMode)
-            .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+            .isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN
     }
 
     @Test
@@ -694,7 +864,7 @@
         val task = createFreeformTask()
         val result = controller.handleRequest(Binder(), createTransition(task))
         assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode)
-            .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+            .isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN
     }
 
     @Test
@@ -706,10 +876,11 @@
 
         val result = controller.handleRequest(Binder(), createTransition(taskDefaultDisplay))
         assertThat(result?.changes?.get(taskDefaultDisplay.token.asBinder())?.windowingMode)
-            .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+            .isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
     fun handleRequest_freeformTask_desktopStashed_returnWCTWithAllAppsBroughtToFront() {
         assumeTrue(ENABLE_SHELL_TRANSITIONS)
         whenever(DesktopModeStatus.isStashingEnabled()).thenReturn(true)
@@ -792,7 +963,56 @@
 
         val result = controller.handleRequest(Binder(), createTransition(task))
         assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode)
-                .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+                .isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun handleRequest_backTransition_singleActiveTask_noToken() {
+        val task = setUpFreeformTask()
+        val result =
+            controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
+        // Doesn't handle request
+        assertThat(result).isNull()
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun handleRequest_backTransition_singleActiveTask_hasToken_desktopWallpaperDisabled() {
+        desktopModeTaskRepository.wallpaperActivityToken = MockToken().token()
+
+        val task = setUpFreeformTask()
+        val result =
+            controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
+        // Doesn't handle request
+        assertThat(result).isNull()
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun handleRequest_backTransition_singleActiveTask_hasToken_desktopWallpaperEnabled() {
+        val wallpaperToken = MockToken().token()
+        desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken
+
+        val task = setUpFreeformTask()
+        val result =
+            controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
+        assertThat(result).isNotNull()
+        // Creates remove wallpaper transaction
+        result!!.assertRemoveAt(index = 0, wallpaperToken)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+    fun handleRequest_backTransition_multipleActiveTasks() {
+        desktopModeTaskRepository.wallpaperActivityToken = MockToken().token()
+
+        val task1 = setUpFreeformTask()
+        setUpFreeformTask()
+        val result =
+            controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))
+        // Doesn't handle request
+        assertThat(result).isNull()
     }
 
     @Test
@@ -895,7 +1115,7 @@
 
         val wct = getLatestExitDesktopWct()
         assertThat(wct.changes[task2.token.asBinder()]?.windowingMode)
-                .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+                .isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN
     }
 
     @Test
@@ -998,6 +1218,9 @@
         assertThat(desktopModeTaskRepository.removeBoundsBeforeMaximize(task.taskId)).isNull()
     }
 
+    private val desktopWallpaperIntent: Intent
+        get() = Intent(context, DesktopWallpaperActivity::class.java)
+
     private fun setUpFreeformTask(
             displayId: Int = DEFAULT_DISPLAY,
             bounds: Rect? = null
@@ -1123,10 +1346,14 @@
     }
 }
 
-private fun WindowContainerTransaction.assertReorderAt(index: Int, task: RunningTaskInfo) {
+private fun WindowContainerTransaction.assertIndexInBounds(index: Int) {
     assertWithMessage("WCT does not have a hierarchy operation at index $index")
         .that(hierarchyOps.size)
         .isGreaterThan(index)
+}
+
+private fun WindowContainerTransaction.assertReorderAt(index: Int, task: RunningTaskInfo) {
+    assertIndexInBounds(index)
     val op = hierarchyOps[index]
     assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_REORDER)
     assertThat(op.container).isEqualTo(task.token.asBinder())
@@ -1137,3 +1364,17 @@
         assertReorderAt(i, tasks[i])
     }
 }
+
+private fun WindowContainerTransaction.assertRemoveAt(index: Int, token: WindowContainerToken) {
+    assertIndexInBounds(index)
+    val op = hierarchyOps[index]
+    assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_REMOVE_TASK)
+    assertThat(op.container).isEqualTo(token.asBinder())
+}
+
+private fun WindowContainerTransaction.assertPendingIntentAt(index: Int, intent: Intent) {
+    assertIndexInBounds(index)
+    val op = hierarchyOps[index]
+    assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_PENDING_INTENT)
+    assertThat(op.pendingIntent?.intent?.component).isEqualTo(intent.component)
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
index e7d37ad..0db10ef 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
@@ -24,6 +24,7 @@
 import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP;
 
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.Mockito.mock;
@@ -167,6 +168,25 @@
     }
 
     @Test
+    public void testStartDragToDesktopDoesNotTriggerCallback() throws RemoteException {
+        TransitionInfo info = mock(TransitionInfo.class);
+        TransitionInfo.Change change = mock(TransitionInfo.Change.class);
+        ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class);
+        when(change.getTaskInfo()).thenReturn(taskInfo);
+        when(info.getChanges()).thenReturn(new ArrayList<>(List.of(change)));
+        when(info.getType()).thenReturn(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP);
+
+        setupTransitionInfo(taskInfo, change, ACTIVITY_TYPE_HOME, TRANSIT_OPEN, true);
+
+        mHomeTransitionObserver.onTransitionReady(mock(IBinder.class),
+                info,
+                mock(SurfaceControl.Transaction.class),
+                mock(SurfaceControl.Transaction.class));
+
+        verify(mListener, times(0)).onHomeVisibilityChanged(anyBoolean());
+    }
+
+    @Test
     public void testHomeActivityWithBackGestureNotifiesHomeIsVisible() throws RemoteException {
         TransitionInfo info = mock(TransitionInfo.class);
         TransitionInfo.Change change = mock(TransitionInfo.Change.class);
diff --git a/libs/androidfw/ZipFileRO.cpp b/libs/androidfw/ZipFileRO.cpp
index 34a6bc2..839c7b6 100644
--- a/libs/androidfw/ZipFileRO.cpp
+++ b/libs/androidfw/ZipFileRO.cpp
@@ -119,30 +119,41 @@
  * appear to be bogus.
  */
 bool ZipFileRO::getEntryInfo(ZipEntryRO entry, uint16_t* pMethod,
+ uint32_t* pUncompLen, uint32_t* pCompLen, off64_t* pOffset,
+ uint32_t* pModWhen, uint32_t* pCrc32) const
+{
+     return getEntryInfo(entry, pMethod, pUncompLen, pCompLen, pOffset, pModWhen,
+      pCrc32, nullptr);
+}
+
+bool ZipFileRO::getEntryInfo(ZipEntryRO entry, uint16_t* pMethod,
     uint32_t* pUncompLen, uint32_t* pCompLen, off64_t* pOffset,
-    uint32_t* pModWhen, uint32_t* pCrc32) const
+    uint32_t* pModWhen, uint32_t* pCrc32, uint16_t* pExtraFieldSize) const
 {
     const _ZipEntryRO* zipEntry = reinterpret_cast<_ZipEntryRO*>(entry);
     const ZipEntry& ze = zipEntry->entry;
 
-    if (pMethod != NULL) {
+    if (pMethod != nullptr) {
         *pMethod = ze.method;
     }
-    if (pUncompLen != NULL) {
+    if (pUncompLen != nullptr) {
         *pUncompLen = ze.uncompressed_length;
     }
-    if (pCompLen != NULL) {
+    if (pCompLen != nullptr) {
         *pCompLen = ze.compressed_length;
     }
-    if (pOffset != NULL) {
+    if (pOffset != nullptr) {
         *pOffset = ze.offset;
     }
-    if (pModWhen != NULL) {
+    if (pModWhen != nullptr) {
         *pModWhen = ze.mod_time;
     }
-    if (pCrc32 != NULL) {
+    if (pCrc32 != nullptr) {
         *pCrc32 = ze.crc32;
     }
+    if (pExtraFieldSize != nullptr) {
+        *pExtraFieldSize = ze.extra_field_size;
+    }
 
     return true;
 }
diff --git a/libs/androidfw/include/androidfw/ZipFileRO.h b/libs/androidfw/include/androidfw/ZipFileRO.h
index 031d2e8..f7c5007 100644
--- a/libs/androidfw/include/androidfw/ZipFileRO.h
+++ b/libs/androidfw/include/androidfw/ZipFileRO.h
@@ -151,6 +151,10 @@
         uint32_t* pCompLen, off64_t* pOffset, uint32_t* pModWhen,
         uint32_t* pCrc32) const;
 
+    bool getEntryInfo(ZipEntryRO entry, uint16_t* pMethod,
+        uint32_t* pUncompLen, uint32_t* pCompLen, off64_t* pOffset,
+        uint32_t* pModWhen, uint32_t* pCrc32, uint16_t* pExtraFieldSize) const;
+
     /*
      * Create a new FileMap object that maps a subset of the archive. For
      * an uncompressed entry this effectively provides a pointer to the
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 34932b1..dc669a5 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -24,7 +24,6 @@
 #include <SkImageAndroid.h>
 #include <SkImageInfo.h>
 #include <SkMatrix.h>
-#include <SkMultiPictureDocument.h>
 #include <SkOverdrawCanvas.h>
 #include <SkOverdrawColorFilter.h>
 #include <SkPicture.h>
@@ -38,6 +37,7 @@
 #include <android-base/properties.h>
 #include <gui/TraceUtils.h>
 #include <include/android/SkSurfaceAndroid.h>
+#include <include/docs/SkMultiPictureDocument.h>
 #include <include/encode/SkPngEncoder.h>
 #include <include/gpu/ganesh/SkSurfaceGanesh.h>
 #include <unistd.h>
@@ -185,7 +185,7 @@
         // we need to keep it until after mMultiPic.close()
         // procs is passed as a pointer, but just as a method of having an optional default.
         // procs doesn't need to outlive this Make call.
-        mMultiPic = SkMakeMultiPictureDocument(mOpenMultiPicStream.get(), &procs,
+        mMultiPic = SkMultiPictureDocument::Make(mOpenMultiPicStream.get(), &procs,
             [sharingCtx = mSerialContext.get()](const SkPicture* pic) {
                     SkSharingSerialContext::collectNonTextureImagesFromPicture(pic, sharingCtx);
             });
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index cf14b1f..823b209 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -18,7 +18,6 @@
 
 #include <SkColorSpace.h>
 #include <SkDocument.h>
-#include <SkMultiPictureDocument.h>
 #include <SkSurface.h>
 
 #include "Lighting.h"
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
index b62711f..21fe6ff 100644
--- a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
@@ -16,10 +16,10 @@
 
 #include "VkFunctorDrawable.h"
 
-#include <GrBackendDrawableInfo.h>
 #include <SkAndroidFrameworkUtils.h>
 #include <SkImage.h>
 #include <SkM44.h>
+#include <include/gpu/ganesh/vk/GrBackendDrawableInfo.h>
 #include <gui/TraceUtils.h>
 #include <private/hwui/DrawVkInfo.h>
 #include <utils/Color.h>
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
index c836df3..520b76e 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
@@ -361,6 +361,7 @@
      * Tests if MR2.SessionCallback.onSessionCreated is called
      * when a route is selected from MR2Manager.
      */
+    @Ignore // Ignored due to flakiness. No plans to fix though, in favor of removal (b/334970551).
     @Test
     public void testRouterOnSessionCreated() throws Exception {
         Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
@@ -512,6 +513,7 @@
     /**
      * Tests select, transfer, release of routes of a provider
      */
+    @Ignore // Ignored due to flakiness. No plans to fix though, in favor of removal (b/334970551).
     @Test
     public void testSelectAndTransferAndRelease() throws Exception {
         Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
@@ -908,6 +910,7 @@
      * Tests if getSelectableRoutes and getDeselectableRoutes filter routes based on
      * selected routes
      */
+    @Ignore // Ignored due to flakiness. No plans to fix though, in favor of removal (b/334970551).
     @Test
     public void testGetSelectableRoutes_notReturnsSelectedRoutes() throws Exception {
         Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
diff --git a/packages/CredentialManager/res/values/strings.xml b/packages/CredentialManager/res/values/strings.xml
index 9fd386f..9db681a 100644
--- a/packages/CredentialManager/res/values/strings.xml
+++ b/packages/CredentialManager/res/values/strings.xml
@@ -68,14 +68,6 @@
   <string name="choose_create_option_password_title">Save password to sign in to <xliff:g id="app_name" example="Tribank">%1$s</xliff:g>?</string>
   <!-- This appears as the title of the modal bottom sheet for users to choose the create option inside a provider when the credential type is others. [CHAR LIMIT=200] -->
   <string name="choose_create_option_sign_in_title">Save sign-in info for <xliff:g id="app_name" example="Tribank">%1$s</xliff:g>?</string>
-  <!-- This appears as a description of the modal bottom sheet when the single tap sign in flow is used for the create passkey flow. [CHAR LIMIT=200] -->
-  <string name="choose_create_single_tap_passkey_title">Use your screen lock to create a passkey for <xliff:g id="app_name" example="Shrine">%1$s</xliff:g>?</string>
-  <!-- This appears as a description of the modal bottom sheet when the single tap sign in flow is used for the create password flow. [CHAR LIMIT=200] -->
-  <string name="choose_create_single_tap_password_title">Use your screen lock to create a password for <xliff:g id="app_name" example="Shrine">%1$s</xliff:g>?</string>
-  <!-- This appears as a description of the modal bottom sheet when the single tap sign in flow is used for the create flow when the credential type is others. [CHAR LIMIT=200] -->
-  <!-- TODO(b/326243891) : Confirm with team on dynamically setting this based on recent product and ux discussions (does not disrupt e2e) -->
-  <string name="choose_create_single_tap_sign_in_title">Use your screen lock to save sign in info for <xliff:g id="app_name" example="Shrine">%1$s</xliff:g>?</string>
-  <!-- Types which are inserted as a placeholder as credentialTypes for other strings. [CHAR LIMIT=200] -->
   <string name="passkey">passkey</string>
   <string name="password">password</string>
   <string name="passkeys">passkeys</string>
@@ -126,15 +118,21 @@
 
   <!-- Strings for the get flow. -->
   <!-- This appears as the title of the modal bottom sheet asking for user confirmation to use the single previously saved passkey to sign in to the app. [CHAR LIMIT=200] -->
-  <string name="get_dialog_title_use_passkey_for">Use your saved passkey for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g>?</string>
+  <string name="get_dialog_title_use_passkey_for">Use your saved passkey for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string>
   <!-- This appears as the title of the modal bottom sheet asking for user confirmation to use the single previously saved password to sign in to the app. [CHAR LIMIT=200] -->
-  <string name="get_dialog_title_use_password_for">Use your saved password for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g>?</string>
+  <string name="get_dialog_title_use_password_for">Use your saved password for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string>
   <!-- This appears as a description of the modal bottom sheet when the single tap sign in flow is used for the get flow. [CHAR LIMIT=200] -->
   <string name="get_dialog_title_single_tap_for">Use your screen lock to sign in to <xliff:g id="app_name" example="Shrine">%1$s</xliff:g> with <xliff:g id="username" example="[email protected]">%2$s</xliff:g></string>
   <!-- This appears as the title of the dialog asking for user confirmation to use the single user credential (previously saved or to be created) to sign in to the app. [CHAR LIMIT=200] -->
-  <string name="get_dialog_title_use_sign_in_for">Use your sign-in for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g>?</string>
+  <string name="get_dialog_title_use_sign_in_for">Use your account for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string>
+  <!-- This appears as the description of the modal bottom sheet asking for user confirmation to use the biometric screen embedded within credential manager for passkey authentication. [CHAR LIMIT=200] -->
+  <string name="get_dialog_description_single_tap_passkey">Sign in to <xliff:g id="app_name" example="YouTube">%1$s</xliff:g> with your saved passkey for <xliff:g id="username" example="[email protected]">%2$s</xliff:g>.</string>
+  <!-- This appears as the description of the modal bottom sheet asking for user confirmation to use the biometric screen embedded within credential manager for password authentication. [CHAR LIMIT=200] -->
+  <string name="get_dialog_description_single_tap_password">Sign in to <xliff:g id="app_name" example="YouTube">%1$s</xliff:g> with your saved password for <xliff:g id="username" example="[email protected]">%2$s</xliff:g>.</string>
+  <!-- This appears as the description of the modal bottom sheet asking for user confirmation to use the biometric screen embedded within credential manager for saved sign-in authentication. [CHAR LIMIT=200] -->
+  <string name="get_dialog_description_single_tap_saved_sign_in">Sign in to <xliff:g id="app_name" example="YouTube">%1$s</xliff:g> with your saved sign-in info for <xliff:g id="username" example="[email protected]">%2$s</xliff:g>.</string>
   <!-- This appears as the title of the dialog asking for user confirmation to unlock / authenticate (e.g. via fingerprint, faceId, passcode etc.) so that we can retrieve their sign-in options. [CHAR LIMIT=200] -->
-  <string name="get_dialog_title_unlock_options_for">Unlock sign-in options for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g>?</string>
+  <string name="get_dialog_title_unlock_options_for">Unlock sign-in options for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string>
   <!-- This appears as the title of the dialog asking for user to make a choice from multiple previously saved passkey to sign in to the app. [CHAR LIMIT=200] -->
   <string name="get_dialog_title_choose_passkey_for">Choose a saved passkey for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string>
   <!-- This appears as the title of the dialog asking for user to make a choice from multiple previously saved passwords to sign in to the app. [CHAR LIMIT=200] -->
@@ -142,7 +140,7 @@
   <!-- This appears as the title of the dialog asking for user to make a choice from multiple previously saved credentials to sign in to the app. [CHAR LIMIT=200] -->
   <string name="get_dialog_title_choose_saved_sign_in_for">Choose a saved sign-in for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string>
   <!-- This appears as the title of the dialog asking for user to make a choice from various available user credentials (previously saved or to be created) to sign in to the app. [CHAR LIMIT=200] -->
-  <string name="get_dialog_title_choose_sign_in_for">Choose a sign-in for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string>
+  <string name="get_dialog_title_choose_sign_in_for">Choose an account for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string>
   <!-- This appears as the title of the dialog asking for user to make a choice from options of available user information (e.g. driver's license, vaccination status) to pass to the app. [CHAR LIMIT=200] -->
   <string name="get_dialog_title_choose_option_for">Choose an option for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g>?</string>
   <!-- This appears as the title of the dialog asking user to send a piece of user information (e.g. driver's license, vaccination status) to the app. [CHAR LIMIT=200] -->
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
index 36f6ad2..429bdbf 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
@@ -270,6 +270,15 @@
         )
     }
 
+    fun getFlowOnMoreOptionOnlySelected() {
+        Log.d(Constants.LOG_TAG, "More Option Only selected")
+        uiState = uiState.copy(
+                getCredentialUiState = uiState.getCredentialUiState?.copy(
+                        currentScreenState = GetScreenState.ALL_SIGN_IN_OPTIONS_ONLY
+                )
+        )
+    }
+
     fun getFlowOnMoreOptionOnSnackBarSelected(isNoAccount: Boolean) {
         Log.d(Constants.LOG_TAG, "More Option on snackBar selected")
         uiState = uiState.copy(
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricHandler.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricHandler.kt
index e088d3a..95f49e9 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricHandler.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricHandler.kt
@@ -17,9 +17,12 @@
 package com.android.credentialmanager.common
 
 import android.content.Context
+import android.content.DialogInterface
 import android.graphics.Bitmap
 import android.hardware.biometrics.BiometricManager
+import android.hardware.biometrics.BiometricManager.Authenticators
 import android.hardware.biometrics.BiometricPrompt
+import android.hardware.biometrics.PromptContentViewWithMoreOptionsButton
 import android.os.CancellationSignal
 import android.util.Log
 import androidx.core.content.ContextCompat.getMainExecutor
@@ -43,19 +46,23 @@
  * Namely, this adds the ability to encapsulate the [providerIcon], the providers icon, the
  * [providerName], which represents the name of the provider, the [displayTitleText] which is
  * the large text displaying the flow in progress, and the [descriptionForCredential], which
- * describes details of where the credential is being saved, and how.
- * (E.g. assume a hypothetical provider 'Any Provider' for *passkey* flows with [email protected]:
+ * describes details of where the credential is being saved, and how. [displaySubtitleText] is only expected
+ * to be used by the 'create' flow, optionally, and describes the saved name of the creating entity.
+ * (E.g. assume a hypothetical provider 'Any Provider' for *passkey* flows with [email protected] and
+ * name 'Your', and an rp called 'The App'):
  *
  * 'get' flow:
  *     - [providerIcon] and [providerName] = 'Any Provider' (and it's icon)
- *     - [displayTitleText] = "Use your saved passkey for Any Provider?"
- *     - [descriptionForCredential] = "Use your screen lock to sign in to Any Provider with
- *     [email protected]"
+ *     - [displayTitleText] = "Use your saved passkey for The App?"
+ *     - [descriptionForCredential] = "Sign in to The App with your saved passkey for
+ *     [email protected]"
  *
  * 'create' flow:
  *     - [providerIcon] and [providerName] = 'Any Provider' (and it's icon)
  *     - [displayTitleText] = "Create passkey to sign in to Any Provider?"
- *     - [descriptionForCredential] = "Use your screen lock to create a passkey for Any Provider?"
+ *     - [subtitle] = "Your"
+ *     - [descriptionForCredential] = "You can use your passkey on other devices. It is saved to
+ *  *     Google Password Manager for [email protected]."
  * ).
  *
  * The above are examples; the credential type can change depending on scenario.
@@ -65,8 +72,9 @@
     val providerIcon: Bitmap,
     val providerName: String,
     val displayTitleText: String,
-    val descriptionForCredential: String,
+    val descriptionForCredential: String?,
     val biometricRequestInfo: BiometricRequestInfo,
+    val displaySubtitleText: CharSequence? = null,
 )
 
 /**
@@ -85,7 +93,7 @@
  * so that should this object exist, the result will be retrievable.
  */
 data class BiometricResult(
-    val biometricAuthenticationResult: BiometricPrompt.AuthenticationResult
+    val biometricAuthenticationResult: BiometricPrompt.AuthenticationResult,
 )
 
 /**
@@ -97,15 +105,6 @@
 )
 
 /**
- * Encapsulates the help callback results to easily manage biometric help states in the flow.
- * To specify, this allows us to parse the onAuthenticationHelp method in the [BiometricPrompt].
- */
-data class BiometricHelp(
-    val helpCode: Int,
-    var helpString: CharSequence? = null
-)
-
-/**
  * This is the entry point to start the integrated biometric prompt for 'get' flows. It captures
  * information specific to the get flow, along with required shared callbacks and more general
  * info across both flows, such as the tapped [EntryInfo] or [sendDataToProvider].
@@ -147,7 +146,7 @@
 
     Log.d(TAG, "The BiometricPrompt API call begins.")
     runBiometricFlow(context, biometricDisplayInfo, callback, openMoreOptionsPage,
-        onBiometricFailureFallback, BiometricFlowType.GET)
+        onBiometricFailureFallback, BiometricFlowType.GET, onCancelFlowAndFinish)
 }
 
 /**
@@ -191,14 +190,15 @@
 
     Log.d(TAG, "The BiometricPrompt API call begins.")
     runBiometricFlow(context, biometricDisplayInfo, callback, openMoreOptionsPage,
-        onBiometricFailureFallback, BiometricFlowType.CREATE)
+        onBiometricFailureFallback, BiometricFlowType.CREATE, onCancelFlowAndFinish)
 }
 
 /**
  * This will handle the logic for integrating credential manager with the biometric prompt for the
  * single account biometric experience. This simultaneously handles both the get and create flows,
  * by retrieving all the data from credential manager, and properly parsing that data into the
- * biometric prompt.
+ * biometric prompt. It will fallback in cases where the biometric api cannot be called, or when
+ * only device credentials are requested.
  */
 private fun runBiometricFlow(
     context: Context,
@@ -206,28 +206,98 @@
     callback: BiometricPrompt.AuthenticationCallback,
     openMoreOptionsPage: () -> Unit,
     onBiometricFailureFallback: (BiometricFlowType) -> Unit,
-    biometricFlowType: BiometricFlowType
+    biometricFlowType: BiometricFlowType,
+    onCancelFlowAndFinish: () -> Unit
 ) {
-    val biometricPrompt = setupBiometricPrompt(context, biometricDisplayInfo, openMoreOptionsPage,
-        biometricDisplayInfo.biometricRequestInfo, biometricFlowType)
-
-    val cancellationSignal = CancellationSignal()
-    cancellationSignal.setOnCancelListener {
-        Log.d(TAG, "Your cancellation signal was called.")
-        // TODO(b/333445112) : Migrate towards passing along the developer cancellation signal
-        // or validate the necessity for this
-    }
-
-    val executor = getMainExecutor(context)
-
     try {
-        biometricPrompt.authenticate(cancellationSignal, executor, callback)
-    } catch (e: IllegalArgumentException) {
+        if (onlyUsingDeviceCredentials(biometricDisplayInfo, context)) {
+            onBiometricFailureFallback(biometricFlowType)
+            return
+        }
+
+        val biometricPrompt = setupBiometricPrompt(context, biometricDisplayInfo,
+            openMoreOptionsPage, biometricDisplayInfo.biometricRequestInfo, onCancelFlowAndFinish)
+
+        val cancellationSignal = CancellationSignal()
+        cancellationSignal.setOnCancelListener {
+            Log.d(TAG, "Your cancellation signal was called.")
+            // TODO(b/333445112) : Migrate towards passing along the developer cancellation signal
+            // or validate the necessity for this
+        }
+
+        val executor = getMainExecutor(context)
+
+        val cryptoOpId = getCryptoOpId(biometricDisplayInfo)
+        if (cryptoOpId != null) {
+            biometricPrompt.authenticate(
+                BiometricPrompt.CryptoObject(cryptoOpId.toLong()),
+                cancellationSignal, executor, callback)
+        } else {
+            biometricPrompt.authenticate(cancellationSignal, executor, callback)
+        }
+    } catch (e: Exception) {
+        // TODO(b/334923201) : Specialize exception catching
         Log.w(TAG, "Calling the biometric prompt API failed with: /n${e.localizedMessage}\n")
         onBiometricFailureFallback(biometricFlowType)
     }
 }
 
+private fun getCryptoOpId(biometricDisplayInfo: BiometricDisplayInfo): Int? {
+    return biometricDisplayInfo.biometricRequestInfo.opId
+}
+
+/**
+ * Determines if, given the allowed authenticators, the flow should fallback early. This has
+ * consistency because for biometrics to exist, **device credentials must exist**. Thus, fallbacks
+ * occur if *only* device credentials are available, to avoid going right into the PIN screen.
+ * Note that if device credential is the only available modality but not requested, or if none
+ * of the requested modalities are available, we propagate the error to the provider instead of
+ * falling back and expect them to handle it as they would prior.
+ * // TODO(b/334197980) : Finalize error propagation/not propagation in real use cases
+ */
+private fun onlyUsingDeviceCredentials(
+    biometricDisplayInfo: BiometricDisplayInfo,
+    context: Context
+): Boolean {
+    val allowedAuthenticators = biometricDisplayInfo.biometricRequestInfo.allowedAuthenticators
+    if (allowedAuthenticators == BiometricManager.Authenticators.DEVICE_CREDENTIAL) {
+        return true
+    }
+
+    val allowedAuthContainsDeviceCredential = containsBiometricAuthenticatorWithDeviceCredentials(
+        allowedAuthenticators)
+
+    if (!allowedAuthContainsDeviceCredential) {
+        // At this point, allowed authenticators is requesting biometrics without device creds.
+        // Thus, a fallback mechanism will be displayed via our own negative button - "cancel".
+        // Beyond this point, fallbacks will occur if none of the stronger authenticators can
+        // be used.
+        return false
+    }
+
+    val biometricManager = context.getSystemService(Context.BIOMETRIC_SERVICE) as BiometricManager
+
+    if (allowedAuthContainsDeviceCredential &&
+        biometricManager.canAuthenticate(Authenticators.BIOMETRIC_WEAK) !=
+        BiometricManager.BIOMETRIC_SUCCESS &&
+        biometricManager.canAuthenticate(Authenticators.BIOMETRIC_STRONG) !=
+        BiometricManager.BIOMETRIC_SUCCESS) {
+        return true
+    }
+
+    return false
+}
+
+private fun containsBiometricAuthenticatorWithDeviceCredentials(
+    allowedAuthenticators: Int
+): Boolean {
+    val allowedAuthContainsDeviceCredential = (allowedAuthenticators ==
+            Authenticators.BIOMETRIC_WEAK or Authenticators.DEVICE_CREDENTIAL) ||
+            (allowedAuthenticators ==
+                    Authenticators.BIOMETRIC_STRONG or Authenticators.DEVICE_CREDENTIAL)
+    return allowedAuthContainsDeviceCredential
+}
+
 /**
  * Sets up the biometric prompt with the UI specific bits.
  * // TODO(b/333445112) : Pass in opId once dependency is confirmed via CryptoObject
@@ -237,49 +307,34 @@
     biometricDisplayInfo: BiometricDisplayInfo,
     openMoreOptionsPage: () -> Unit,
     biometricRequestInfo: BiometricRequestInfo,
-    biometricFlowType: BiometricFlowType,
+    onCancelFlowAndFinish: () -> Unit
 ): BiometricPrompt {
-    val finalAuthenticators = removeDeviceCredential(biometricRequestInfo.allowedAuthenticators)
+    val listener =
+        DialogInterface.OnClickListener { _: DialogInterface?, _: Int -> openMoreOptionsPage() }
 
-    val biometricPrompt = BiometricPrompt.Builder(context)
+    val promptContentViewBuilder = PromptContentViewWithMoreOptionsButton.Builder()
+        .setMoreOptionsButtonListener(context.mainExecutor, listener)
+    biometricDisplayInfo.descriptionForCredential?.let {
+        promptContentViewBuilder.setDescription(it) }
+
+    val biometricPromptBuilder = BiometricPrompt.Builder(context)
         .setTitle(biometricDisplayInfo.displayTitleText)
-        // TODO(b/333445112) : Migrate to using new methods and strings recently aligned upon
-        .setNegativeButton(context.getString(if (biometricFlowType == BiometricFlowType.GET)
-            R.string
-                .dropdown_presentation_more_sign_in_options_text else R.string.string_more_options),
-            getMainExecutor(context)) { _, _ ->
-            openMoreOptionsPage()
-        }
-        .setAllowedAuthenticators(finalAuthenticators)
+        .setAllowedAuthenticators(biometricRequestInfo.allowedAuthenticators)
         .setConfirmationRequired(true)
         .setLogoBitmap(biometricDisplayInfo.providerIcon)
         .setLogoDescription(biometricDisplayInfo.providerName)
-        .setDescription(biometricDisplayInfo.descriptionForCredential)
-        .build()
+        .setContentView(promptContentViewBuilder.build())
 
-    return biometricPrompt
-}
-
-// TODO(b/333445112) : Remove after larger level alignments made on fallback negative button
-// For the time being, we do not support the pin fallback until UX is decided.
-private fun removeDeviceCredential(requestAllowedAuthenticators: Int): Int {
-    var finalAuthenticators = requestAllowedAuthenticators
-
-    if (requestAllowedAuthenticators == (BiometricManager.Authenticators.DEVICE_CREDENTIAL or
-                BiometricManager.Authenticators.BIOMETRIC_WEAK)) {
-        finalAuthenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK
+    if (!containsBiometricAuthenticatorWithDeviceCredentials(biometricDisplayInfo
+            .biometricRequestInfo.allowedAuthenticators)) {
+        biometricPromptBuilder.setNegativeButton(context.getString(R.string.string_cancel),
+            getMainExecutor(context)
+        ) { _: DialogInterface?, _: Int -> onCancelFlowAndFinish() }
     }
 
-    if (requestAllowedAuthenticators == (BiometricManager.Authenticators.DEVICE_CREDENTIAL or
-                BiometricManager.Authenticators.BIOMETRIC_STRONG)) {
-        finalAuthenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK
-    }
+    biometricDisplayInfo.displaySubtitleText?.let { biometricPromptBuilder.setSubtitle(it) }
 
-    if (requestAllowedAuthenticators == (BiometricManager.Authenticators.DEVICE_CREDENTIAL)) {
-        finalAuthenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK
-    }
-
-    return finalAuthenticators
+    return biometricPromptBuilder.build()
 }
 
 /**
@@ -417,15 +472,29 @@
     }
     val singleEntryType = selectedEntry.credentialType
     val username = selectedEntry.userName
+
+    // TODO(b/330396140) : Finalize localization and parsing for specific sign in option flows
+    //  (fingerprint, face, etc...))
     displayTitleText = context.getString(
         generateDisplayTitleTextResCode(singleEntryType),
         getRequestDisplayInfo.appName
     )
+
     descriptionText = context.getString(
-        R.string.get_dialog_title_single_tap_for,
+        when (singleEntryType) {
+            CredentialType.PASSKEY ->
+                R.string.get_dialog_description_single_tap_passkey
+
+            CredentialType.PASSWORD ->
+                R.string.get_dialog_description_single_tap_password
+
+            CredentialType.UNKNOWN ->
+                R.string.get_dialog_description_single_tap_saved_sign_in
+        },
         getRequestDisplayInfo.appName,
         username
     )
+
     return BiometricDisplayInfo(providerIcon = icon, providerName = providerName,
         displayTitleText = displayTitleText, descriptionForCredential = descriptionText,
         biometricRequestInfo = selectedEntry.biometricRequest as BiometricRequestInfo)
@@ -451,23 +520,12 @@
         getCreateTitleResCode(createRequestDisplayInfo),
         createRequestDisplayInfo.appName
     )
-    val descriptionText: String = context.getString(
-        when (createRequestDisplayInfo.type) {
-            CredentialType.PASSKEY ->
-                R.string.choose_create_single_tap_passkey_title
 
-            CredentialType.PASSWORD ->
-                R.string.choose_create_single_tap_password_title
-
-            CredentialType.UNKNOWN ->
-                R.string.choose_create_single_tap_sign_in_title
-        },
-        createRequestDisplayInfo.appName,
-    )
-    // TODO(b/333445112) : Add a subtitle and any other recently aligned ideas
+    // TODO(b/330396140) : If footerDescription is null, determine if we need to fallback
     return BiometricDisplayInfo(providerIcon = icon, providerName = providerName,
-        displayTitleText = displayTitleText, descriptionForCredential = descriptionText,
-        biometricRequestInfo = selectedEntry.biometricRequest as BiometricRequestInfo)
+        displayTitleText = displayTitleText, descriptionForCredential = selectedEntry
+            .footerDescription, biometricRequestInfo = selectedEntry.biometricRequest
+                as BiometricRequestInfo, displaySubtitleText = createRequestDisplayInfo.title)
 }
 
 /**
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt
index d13d86f..149c14a 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt
@@ -349,6 +349,38 @@
     }
 }
 
+@Composable
+fun MoreOptionTopAppBarWithCustomNavigation(
+        text: String,
+        onNavigationIconClicked: () -> Unit,
+        navigationIcon: ImageVector,
+        navigationIconContentDescription: String,
+        bottomPadding: Dp,
+) {
+    Row(
+            modifier = Modifier.padding(top = 12.dp, bottom = bottomPadding),
+            verticalAlignment = Alignment.CenterVertically,
+    ) {
+        IconButton(
+                modifier = Modifier.padding(top = 8.dp, bottom = 8.dp, start = 4.dp).size(48.dp),
+                onClick = onNavigationIconClicked
+        ) {
+            Box(
+                    modifier = Modifier.size(48.dp),
+                    contentAlignment = Alignment.Center,
+            ) {
+                Icon(
+                        imageVector = navigationIcon,
+                        contentDescription = navigationIconContentDescription,
+                        modifier = Modifier.size(24.dp).autoMirrored(),
+                        tint = LocalAndroidColorScheme.current.onSurfaceVariant,
+                )
+            }
+        }
+        LargeTitleText(text = text, modifier = Modifier.padding(horizontal = 4.dp))
+    }
+}
+
 private fun Modifier.autoMirrored() = composed {
     when (LocalLayoutDirection.current) {
         LayoutDirection.Rtl -> graphicsLayer(scaleX = -1f)
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
index c1120bb3..ce4f402 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
@@ -32,6 +32,7 @@
 import androidx.compose.foundation.layout.wrapContentHeight
 import androidx.compose.foundation.lazy.items
 import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Close
 import androidx.compose.material.icons.outlined.QrCodeScanner
 import androidx.compose.material3.Divider
 import androidx.compose.material3.TextButton
@@ -70,6 +71,7 @@
 import com.android.credentialmanager.common.ui.LargeLabelTextOnSurfaceVariant
 import com.android.credentialmanager.common.ui.ModalBottomSheet
 import com.android.credentialmanager.common.ui.MoreOptionTopAppBar
+import com.android.credentialmanager.common.ui.MoreOptionTopAppBarWithCustomNavigation
 import com.android.credentialmanager.common.ui.SheetContainerCard
 import com.android.credentialmanager.common.ui.Snackbar
 import com.android.credentialmanager.common.ui.SnackbarActionText
@@ -148,7 +150,7 @@
                                 .currentScreenState == GetScreenState.BIOMETRIC_SELECTION) {
                             BiometricSelectionPage(
                                 biometricEntry = getCredentialUiState.activeEntry,
-                                onMoreOptionSelected = viewModel::getFlowOnMoreOptionSelected,
+                                onMoreOptionSelected = viewModel::getFlowOnMoreOptionOnlySelected,
                                 onCancelFlowAndFinish = viewModel::onUserCancel,
                                 onIllegalStateAndFinish = viewModel::onIllegalUiState,
                                 requestDisplayInfo = getCredentialUiState.requestDisplayInfo,
@@ -163,6 +165,28 @@
                                 onBiometricPromptStateChange =
                                 viewModel::onBiometricPromptStateChange
                             )
+                        } else if (credmanBiometricApiEnabled() &&
+                                getCredentialUiState.currentScreenState
+                                == GetScreenState.ALL_SIGN_IN_OPTIONS_ONLY) {
+                            AllSignInOptionCard(
+                                    providerInfoList = getCredentialUiState.providerInfoList,
+                                    providerDisplayInfo = getCredentialUiState.providerDisplayInfo,
+                                    onEntrySelected = viewModel::getFlowOnEntrySelected,
+                                    onBackButtonClicked = viewModel::onUserCancel,
+                                    onCancel = viewModel::onUserCancel,
+                                    onLog = { viewModel.logUiEvent(it) },
+                                    customTopBar = { MoreOptionTopAppBarWithCustomNavigation(
+                                            text = stringResource(
+                                                    R.string.get_dialog_title_sign_in_options),
+                                            onNavigationIconClicked = viewModel::onUserCancel,
+                                            navigationIcon = Icons.Filled.Close,
+                                            navigationIconContentDescription =
+                                            stringResource(R.string.accessibility_close_button),
+                                            bottomPadding = 0.dp
+                                    ) }
+                            )
+                            viewModel.uiMetrics.log(GetCredentialEvent
+                                    .CREDMAN_GET_CRED_SCREEN_ALL_SIGN_IN_OPTIONS)
                         } else {
                             AllSignInOptionCard(
                                 providerInfoList = getCredentialUiState.providerInfoList,
@@ -525,19 +549,8 @@
                                 R.string.get_dialog_title_choose_password_for
                             else if (areAllPasskeysOnPrimaryScreen)
                                 R.string.get_dialog_title_choose_passkey_for
-                            else if (primaryPageLockedEntryList.isNotEmpty() ||
-                                primaryPageCredentialEntryList.any {
-                                    it.sortedCredentialEntryList.first().credentialType !=
-                                            CredentialType.PASSWORD &&
-                                    it.sortedCredentialEntryList.first().credentialType !=
-                                            CredentialType.PASSKEY
-                                }
-                            ) // An unknown typed / locked entry exists, and we can't say it is
-                            // already saved, strictly speaking. Hence use a different title
-                            // without the mention of "saved".
+                            else
                                 R.string.get_dialog_title_choose_sign_in_for
-                            else // All entries on the primary screen are passkeys or passwords
-                                R.string.get_dialog_title_choose_saved_sign_in_for
                         },
                         requestDisplayInfo.appName
                     ),
@@ -642,7 +655,13 @@
     return providerId
 }
 
-/** Draws the secondary credential selection page, where all sign-in options are listed. */
+/**
+ * Draws the secondary credential selection page, where all sign-in options are listed.
+ *
+ * By default, this card has 'back' navigation whereby user can navigate back to invoke
+ * [onBackButtonClicked]. However if a different top bar with possibly a different navigation
+ * is required, then the caller of this Composable can set a [customTopBar].
+ */
 @Composable
 fun AllSignInOptionCard(
     providerInfoList: List<ProviderInfo>,
@@ -651,16 +670,21 @@
     onBackButtonClicked: () -> Unit,
     onCancel: () -> Unit,
     onLog: @Composable (UiEventEnum) -> Unit,
+    customTopBar: (@Composable() () -> Unit)? = null
 ) {
     val sortedUserNameToCredentialEntryList =
         providerDisplayInfo.sortedUserNameToCredentialEntryList
     val authenticationEntryList = providerDisplayInfo.authenticationEntryList
     SheetContainerCard(topAppBar = {
-        MoreOptionTopAppBar(
-            text = stringResource(R.string.get_dialog_title_sign_in_options),
-            onNavigationIconClicked = onBackButtonClicked,
-            bottomPadding = 0.dp,
-        )
+        if (customTopBar != null) {
+            customTopBar()
+        } else {
+            MoreOptionTopAppBar(
+                    text = stringResource(R.string.get_dialog_title_sign_in_options),
+                    onNavigationIconClicked = onBackButtonClicked,
+                    bottomPadding = 0.dp,
+            )
+        }
     }) {
         var isFirstSection = true
         // For username
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
index b03407b..8e78861 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
@@ -163,7 +163,11 @@
     /** The single tap biometric selection page. */
     BIOMETRIC_SELECTION,
 
-    /** The secondary credential selection page, where all sign-in options are listed. */
+    /**
+     * The secondary credential selection page, where all sign-in options are listed.
+     *
+     * This state is expected to go back to PRIMARY_SELECTION on back navigation
+     */
     ALL_SIGN_IN_OPTIONS,
 
     /** The snackbar only page when there's no account but only a remoteEntry. */
@@ -171,6 +175,14 @@
 
     /** The snackbar when there are only auth entries and all of them turn out to be empty. */
     UNLOCKED_AUTH_ENTRIES_ONLY,
+
+    /**
+     * The secondary credential selection page, where all sign-in options are listed.
+     *
+     * This state has no option for the user to navigate back to PRIMARY_SELECTION, and
+     * instead can be terminated independently.
+     */
+    ALL_SIGN_IN_OPTIONS_ONLY,
 }
 
 
@@ -285,7 +297,7 @@
         providerDisplayInfo.remoteEntry != null)
         GetScreenState.REMOTE_ONLY
     else if (isRequestForAllOptions)
-        GetScreenState.ALL_SIGN_IN_OPTIONS
+        GetScreenState.ALL_SIGN_IN_OPTIONS_ONLY
     else if (isBiometricFlow(providerDisplayInfo, isFlowAutoSelectable(providerDisplayInfo)))
         GetScreenState.BIOMETRIC_SELECTION
     else GetScreenState.PRIMARY_SELECTION
diff --git a/packages/PackageInstaller/res/values-af/strings.xml b/packages/PackageInstaller/res/values-af/strings.xml
index 140fa36..13661e3 100644
--- a/packages/PackageInstaller/res/values-af/strings.xml
+++ b/packages/PackageInstaller/res/values-af/strings.xml
@@ -58,7 +58,7 @@
     <string name="uninstall_application_title" msgid="4045420072401428123">"Deïnstalleer program"</string>
     <string name="uninstall_update_title" msgid="824411791011583031">"Deïnstalleer opdatering"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> is deel van die volgende program:"</string>
-    <string name="uninstall_application_text" msgid="3816830743706143980">"Wil jy hierdie program deïnstalleer?"</string>
+    <string name="uninstall_application_text" msgid="3816830743706143980">"Wil jy hierdie app deïnstalleer?"</string>
     <string name="archive_application_text" msgid="8482325710714386348">"Jou persoonlike data sal gestoor word"</string>
     <string name="archive_application_text_all_users" msgid="3151229641681672580">"Argiveer hierdie app vir alle gebruikers? Jou persoonlike data sal gestoor word"</string>
     <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Argiveer hierdie app op jou werkprofiel? Jou persoonlike data sal gestoor word"</string>
diff --git a/packages/PackageInstaller/res/values-gu/strings.xml b/packages/PackageInstaller/res/values-gu/strings.xml
index f642e145..82d5414 100644
--- a/packages/PackageInstaller/res/values-gu/strings.xml
+++ b/packages/PackageInstaller/res/values-gu/strings.xml
@@ -58,7 +58,7 @@
     <string name="uninstall_application_title" msgid="4045420072401428123">"ઍપ્લિકેશન અનઇન્સ્ટૉલ કરો"</string>
     <string name="uninstall_update_title" msgid="824411791011583031">"અપડેટ અનઇન્સ્ટૉલ કરો"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g>, નીચેની ઍપ્લિકેશનનો ભાગ છે:"</string>
-    <string name="uninstall_application_text" msgid="3816830743706143980">"શું તમે આ ઍપને અનઇન્સ્ટૉલ કરવા માંગો છો?"</string>
+    <string name="uninstall_application_text" msgid="3816830743706143980">"શું તમે આ ઍપને અનઇન્સ્ટૉલ કરવા માગો છો?"</string>
     <string name="archive_application_text" msgid="8482325710714386348">"તમારો વ્યક્તિગત ડેટા સાચવવામાં આવશે"</string>
     <string name="archive_application_text_all_users" msgid="3151229641681672580">"શું આ ઍપને તમામ વપરાશકર્તાઓ માટે આર્કાઇવ કરીએ? તમારો વ્યક્તિગત ડેટા સાચવવામાં આવશે"</string>
     <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"શું આ ઍપને તમારી ઑફિસની પ્રોફાઇલ પર આર્કાઇવ કરીએ? તમારો વ્યક્તિગત ડેટા સાચવવામાં આવશે"</string>
diff --git a/packages/PackageInstaller/res/values-hy/strings.xml b/packages/PackageInstaller/res/values-hy/strings.xml
index f2bc41e..d9fb570 100644
--- a/packages/PackageInstaller/res/values-hy/strings.xml
+++ b/packages/PackageInstaller/res/values-hy/strings.xml
@@ -58,7 +58,7 @@
     <string name="uninstall_application_title" msgid="4045420072401428123">"Հավելվածի ապատեղադրում"</string>
     <string name="uninstall_update_title" msgid="824411791011583031">"Ապատեղադրել թարմացումը"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> գործողությունը հետևյալ հավելվածի մասն է`"</string>
-    <string name="uninstall_application_text" msgid="3816830743706143980">"Ուզո՞ւմ եք ապատեղադրել այս հավելվածը։"</string>
+    <string name="uninstall_application_text" msgid="3816830743706143980">"Ուզում եք ապատեղադրե՞լ այս հավելվածը։"</string>
     <string name="archive_application_text" msgid="8482325710714386348">"Ձեր անձնական տվյալները կպահվեն"</string>
     <string name="archive_application_text_all_users" msgid="3151229641681672580">"Արխիվացնե՞լ այս հավելվածը բոլոր օգտատերերի համար։ Ձեր անձնական տվյալները կպահվեն"</string>
     <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Արխիվացնե՞լ այս հավելվածը ձեր աշխատանքային պրոֆիլում։ Ձեր անձնական տվյալները կպահվեն"</string>
diff --git a/packages/PrintSpooler/res/values-night/themes.xml b/packages/PrintSpooler/res/values-night/themes.xml
index 4428dbb..3cc64a6 100644
--- a/packages/PrintSpooler/res/values-night/themes.xml
+++ b/packages/PrintSpooler/res/values-night/themes.xml
@@ -30,6 +30,7 @@
         <item name="android:windowIsTranslucent">true</item>
         <item name="android:windowActionBar">false</item>
         <item name="android:windowNoTitle">true</item>
+        <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
     </style>
 
 </resources>
diff --git a/packages/PrintSpooler/res/values/themes.xml b/packages/PrintSpooler/res/values/themes.xml
index 4dcad10..bd96025 100644
--- a/packages/PrintSpooler/res/values/themes.xml
+++ b/packages/PrintSpooler/res/values/themes.xml
@@ -31,6 +31,7 @@
         <item name="android:windowActionBar">false</item>
         <item name="android:windowNoTitle">true</item>
         <item name="android:windowLightStatusBar">true</item>
+        <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
     </style>
 
 </resources>
diff --git a/packages/SettingsLib/DataStore/Android.bp b/packages/SettingsLib/DataStore/Android.bp
index 9fafcab..86c8f0da 100644
--- a/packages/SettingsLib/DataStore/Android.bp
+++ b/packages/SettingsLib/DataStore/Android.bp
@@ -2,12 +2,17 @@
     default_applicable_licenses: ["frameworks_base_license"],
 }
 
+filegroup {
+    name: "SettingsLibDataStore-srcs",
+    srcs: ["src/**/*"],
+}
+
 android_library {
     name: "SettingsLibDataStore",
     defaults: [
         "SettingsLintDefaults",
     ],
-    srcs: ["src/**/*"],
+    srcs: [":SettingsLibDataStore-srcs"],
     static_libs: [
         "androidx.annotation_annotation",
         "androidx.collection_collection-ktx",
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreFileArchiver.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreFileArchiver.kt
index 9d3fb66..7644bc9 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreFileArchiver.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreFileArchiver.kt
@@ -19,6 +19,7 @@
 import android.app.backup.BackupDataInputStream
 import android.content.Context
 import android.util.Log
+import androidx.annotation.VisibleForTesting
 import java.io.File
 import java.io.InputStream
 import java.io.OutputStream
@@ -33,11 +34,9 @@
  */
 internal class BackupRestoreFileArchiver(
     private val context: Context,
-    private val fileStorages: List<BackupRestoreFileStorage>,
+    @get:VisibleForTesting internal val fileStorages: List<BackupRestoreFileStorage>,
+    override val name: String,
 ) : BackupRestoreStorage() {
-    override val name: String
-        get() = "file_archiver"
-
     override fun createBackupRestoreEntities(): List<BackupRestoreEntity> =
         fileStorages.map { it.toBackupRestoreEntity() }
 
@@ -88,7 +87,8 @@
     }
 }
 
-private fun BackupRestoreFileStorage.toBackupRestoreEntity() =
+@VisibleForTesting
+internal fun BackupRestoreFileStorage.toBackupRestoreEntity() =
     object : BackupRestoreEntity {
         override val key: String
             get() = storageFilePath
@@ -107,7 +107,7 @@
                 Log.i(LOG_TAG, "[$name] $key not exist")
                 return EntityBackupResult.DELETE
             }
-            val codec = codec() ?: defaultCodec()
+            val codec = defaultCodec()
             // MUST close to flush the data
             wrapBackupOutputStream(codec, outputStream).use { stream ->
                 val bytesCopied = file.inputStream().use { it.copyTo(stream) }
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorage.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorage.kt
index c4c00cb..935f9cc 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorage.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorage.kt
@@ -22,6 +22,7 @@
 import android.app.backup.BackupHelper
 import android.os.ParcelFileDescriptor
 import android.util.Log
+import androidx.annotation.VisibleForTesting
 import androidx.collection.MutableScatterMap
 import com.google.common.io.ByteStreams
 import java.io.ByteArrayOutputStream
@@ -60,10 +61,11 @@
      *
      * Map key is the entity key, map value is the checksum of backup data.
      */
-    protected val entityStates = MutableScatterMap<String, Long>()
+    @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
+    val entityStates = MutableScatterMap<String, Long>()
 
     /** Entities created by [createBackupRestoreEntities]. This field is for restore only. */
-    private var entities: List<BackupRestoreEntity>? = null
+    @VisibleForTesting internal var entities: List<BackupRestoreEntity>? = null
 
     /** Entities to back up and restore. */
     abstract fun createBackupRestoreEntities(): List<BackupRestoreEntity>
@@ -76,7 +78,7 @@
         data: BackupDataOutput,
         newState: ParcelFileDescriptor,
     ) {
-        oldState.readEntityStates(entityStates)
+        readEntityStates(oldState, entityStates)
         val backupContext = BackupContext(data)
         if (!enableBackup(backupContext)) {
             Log.i(LOG_TAG, "[$name] Backup disabled")
@@ -94,7 +96,10 @@
             val codec = entity.codec() ?: defaultCodec()
             val result =
                 try {
-                    entity.backup(backupContext, wrapBackupOutputStream(codec, checkedOutputStream))
+                    // MUST close to flush all data
+                    wrapBackupOutputStream(codec, checkedOutputStream).use {
+                        entity.backup(backupContext, it)
+                    }
                 } catch (exception: Exception) {
                     Log.e(LOG_TAG, "[$name] Fail to backup entity $key", exception)
                     continue
@@ -191,9 +196,13 @@
     /** Callbacks when restore finished. */
     open fun onRestoreFinished() {}
 
-    private fun ParcelFileDescriptor?.readEntityStates(state: MutableScatterMap<String, Long>) {
+    @VisibleForTesting
+    internal fun readEntityStates(
+        parcelFileDescriptor: ParcelFileDescriptor?,
+        state: MutableScatterMap<String, Long>,
+    ) {
         state.clear()
-        if (this == null) return
+        val fileDescriptor = parcelFileDescriptor?.fileDescriptor ?: return
         // do not close the streams
         val fileInputStream = FileInputStream(fileDescriptor)
         val dataInputStream = DataInputStream(fileInputStream)
@@ -233,6 +242,7 @@
                 dataOutputStream.writeUTF(key)
                 dataOutputStream.writeLong(value)
             }
+            dataOutputStream.flush()
         } catch (exception: Exception) {
             Log.e(LOG_TAG, "[$name] Fail to write state file", exception)
         }
@@ -241,7 +251,7 @@
     }
 
     companion object {
-        private const val STATE_VERSION: Byte = 0
+        internal const val STATE_VERSION: Byte = 0
 
         /** Checksum for entity backup data. */
         fun createChecksum(): Checksum = CRC32()
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorageManager.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorageManager.kt
index cfdcaff..8242347 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorageManager.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorageManager.kt
@@ -21,23 +21,32 @@
 import android.app.backup.BackupManager
 import android.content.Context
 import android.util.Log
+import androidx.annotation.VisibleForTesting
 import com.google.common.util.concurrent.MoreExecutors
 import java.util.concurrent.ConcurrentHashMap
 
 /** Manager of [BackupRestoreStorage]. */
 class BackupRestoreStorageManager private constructor(private val application: Application) {
-    private val storageWrappers = ConcurrentHashMap<String, StorageWrapper>()
+    @VisibleForTesting internal val storageWrappers = ConcurrentHashMap<String, StorageWrapper>()
 
     private val executor = MoreExecutors.directExecutor()
 
     /**
      * Adds all the registered [BackupRestoreStorage] as the helpers of given [BackupAgentHelper].
      *
-     * All [BackupRestoreFileStorage]s will be wrapped as a single [BackupRestoreFileArchiver].
+     * All [BackupRestoreFileStorage]s will be wrapped as a single [BackupRestoreFileArchiver],
+     * specify [fileArchiverName] to avoid key prefix conflict if needed.
      *
+     * @param backupAgentHelper backup agent helper to add helpers
+     * @param fileArchiverName key prefix of the [BackupRestoreFileArchiver], the value must not be
+     *   changed in future
      * @see BackupAgentHelper.addHelper
      */
-    fun addBackupAgentHelpers(backupAgentHelper: BackupAgentHelper) {
+    @JvmOverloads
+    fun addBackupAgentHelpers(
+        backupAgentHelper: BackupAgentHelper,
+        fileArchiverName: String = "file_archiver",
+    ) {
         val fileStorages = mutableListOf<BackupRestoreFileStorage>()
         for ((keyPrefix, storageWrapper) in storageWrappers) {
             val storage = storageWrapper.storage
@@ -48,7 +57,7 @@
             }
         }
         // Always add file archiver even fileStorages is empty to handle forward compatibility
-        val fileArchiver = BackupRestoreFileArchiver(application, fileStorages)
+        val fileArchiver = BackupRestoreFileArchiver(application, fileStorages, fileArchiverName)
         backupAgentHelper.addHelper(fileArchiver.name, fileArchiver)
     }
 
@@ -106,7 +115,8 @@
     /** Returns storage with given name, exception is raised if not found. */
     fun getOrThrow(name: String): BackupRestoreStorage = storageWrappers[name]!!.storage
 
-    private inner class StorageWrapper(val storage: BackupRestoreStorage) :
+    @VisibleForTesting
+    internal inner class StorageWrapper(val storage: BackupRestoreStorage) :
         Observer, KeyedObserver<Any?> {
         init {
             when (storage) {
@@ -139,7 +149,7 @@
                 LOG_TAG,
                 "Notify BackupManager dataChanged: storage=$name key=$key reason=$reason"
             )
-            BackupManager.dataChanged(application.packageName)
+            BackupManager(application).dataChanged()
         }
 
         fun removeObserver() {
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesStorage.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesStorage.kt
index 0c1b417..9f9c0d8 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesStorage.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesStorage.kt
@@ -20,9 +20,11 @@
 import android.content.SharedPreferences
 import android.os.Build
 import android.util.Log
-import androidx.core.content.ContextCompat
+import androidx.annotation.VisibleForTesting
 import java.io.File
 
+private fun defaultVerbose() = Build.TYPE == "eng"
+
 /**
  * [SharedPreferences] based storage.
  *
@@ -43,24 +45,35 @@
  * @param verbose Verbose logging on key/value pairs during backup/restore. Enable for dev only!
  * @param filter Filter of key/value pairs for backup and restore.
  */
-class SharedPreferencesStorage
+open class SharedPreferencesStorage
 @JvmOverloads
 constructor(
     context: Context,
     override val name: String,
-    mode: Int,
-    private val verbose: Boolean = (Build.TYPE == "eng"),
+    @get:VisibleForTesting internal val sharedPreferences: SharedPreferences,
+    private val codec: BackupCodec? = null,
+    private val verbose: Boolean = defaultVerbose(),
     private val filter: (String, Any?) -> Boolean = { _, _ -> true },
 ) :
     BackupRestoreFileStorage(context, context.getSharedPreferencesFilePath(name)),
     KeyedObservable<String> by KeyedDataObservable() {
 
-    private val sharedPreferences = context.getSharedPreferences(name, mode)
+    @JvmOverloads
+    constructor(
+        context: Context,
+        name: String,
+        mode: Int,
+        codec: BackupCodec? = null,
+        verbose: Boolean = defaultVerbose(),
+        filter: (String, Any?) -> Boolean = { _, _ -> true },
+    ) : this(context, name, context.getSharedPreferences(name, mode), codec, verbose, filter)
 
     /** Name of the intermediate SharedPreferences. */
-    private val intermediateName: String
+    @VisibleForTesting
+    internal val intermediateName: String
         get() = "_br_$name"
 
+    @Suppress("DEPRECATION")
     private val intermediateSharedPreferences: SharedPreferences
         get() {
             // use MODE_MULTI_PROCESS to ensure a reload
@@ -82,12 +95,15 @@
         sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferencesListener)
     }
 
+    override fun defaultCodec() = codec ?: super.defaultCodec()
+
     override val backupFile: File
         // use a different file to avoid multi-thread file write
         get() = context.getSharedPreferencesFile(intermediateName)
 
     override fun prepareBackup(file: File) {
-        val editor = intermediateSharedPreferences.merge(sharedPreferences.all, "Backup")
+        val editor =
+            mergeSharedPreferences(intermediateSharedPreferences, sharedPreferences.all, "Backup")
         // commit to ensure data is write to disk synchronously
         if (!editor.commit()) {
             Log.w(LOG_TAG, "[$name] fail to commit")
@@ -104,8 +120,8 @@
         // observers consistently once restore finished.
         sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferencesListener)
         val restored = intermediateSharedPreferences
-        val editor = sharedPreferences.merge(restored.all, "Restore")
-        editor.apply() // apply to avoid blocking
+        val editor = mergeSharedPreferences(sharedPreferences, restored.all, "Restore")
+        editor.commit() // commit to avoid race condition
         sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferencesListener)
         // clear the intermediate SharedPreferences
         restored.delete(intermediateName)
@@ -115,7 +131,7 @@
         if (deleteSharedPreferences(name)) {
             Log.i(LOG_TAG, "SharedPreferences $name deleted")
         } else {
-            edit().clear().apply()
+            edit().clear().commit() // commit to avoid potential race condition
         }
     }
 
@@ -126,11 +142,13 @@
             false
         }
 
-    private fun SharedPreferences.merge(
+    @VisibleForTesting
+    internal open fun mergeSharedPreferences(
+        sharedPreferences: SharedPreferences,
         entries: Map<String, Any?>,
-        operation: String
+        operation: String,
     ): SharedPreferences.Editor {
-        val editor = edit()
+        val editor = sharedPreferences.edit()
         for ((key, value) in entries) {
             if (!filter.invoke(key, value)) {
                 if (verbose) Log.v(LOG_TAG, "[$name] $operation skips $key=$value")
@@ -184,7 +202,7 @@
     companion object {
         private fun Context.getSharedPreferencesFilePath(name: String): String {
             val file = getSharedPreferencesFile(name)
-            return file.relativeTo(ContextCompat.getDataDir(this)!!).toString()
+            return file.relativeTo(dataDirCompat).toString()
         }
 
         /** Returns the absolute path of shared preferences file. */
diff --git a/packages/SettingsLib/DataStore/tests/Android.bp b/packages/SettingsLib/DataStore/tests/Android.bp
index 8770dfa..5d000eb 100644
--- a/packages/SettingsLib/DataStore/tests/Android.bp
+++ b/packages/SettingsLib/DataStore/tests/Android.bp
@@ -9,11 +9,16 @@
 
 android_robolectric_test {
     name: "SettingsLibDataStoreTest",
-    srcs: ["src/**/*"],
+    srcs: [
+        ":SettingsLibDataStore-srcs", // b/240432457
+        "src/**/*",
+    ],
     static_libs: [
-        "SettingsLibDataStore",
+        "androidx.collection_collection-ktx",
+        "androidx.core_core-ktx",
         "androidx.test.ext.junit",
         "guava",
+        "kotlin-test",
         "mockito-robolectric-prebuilt", // mockito deps order matters!
         "mockito-kotlin2",
     ],
diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupCodecTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupCodecTest.kt
new file mode 100644
index 0000000..867831b
--- /dev/null
+++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupCodecTest.kt
@@ -0,0 +1,75 @@
+/*
+ * 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.settingslib.datastore
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import java.io.ByteArrayInputStream
+import java.io.ByteArrayOutputStream
+import kotlin.random.Random
+import kotlin.test.assertFailsWith
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** Tests of [BackupCodec]. */
+@RunWith(AndroidJUnit4::class)
+class BackupCodecTest {
+    @Test
+    fun name() {
+        val names = mutableSetOf<String>()
+        for (codec in allCodecs()) {
+            assertThat(names).doesNotContain(codec.name)
+            names.add(codec.name)
+        }
+    }
+
+    @Test
+    fun fromId() {
+        for (codec in allCodecs()) {
+            assertThat(BackupCodec.fromId(codec.id)).isInstanceOf(codec::class.java)
+        }
+    }
+
+    @Test
+    fun fromId_unknownId() {
+        assertFailsWith(IllegalArgumentException::class) { BackupCodec.fromId(-1) }
+    }
+
+    @Test
+    fun encode_decode() {
+        val random = Random.Default
+        fun test(codec: BackupCodec, size: Int) {
+            val data = random.nextBytes(size)
+
+            // encode
+            val outputStream = ByteArrayOutputStream()
+            codec.encode(outputStream).use { it.write(data) }
+
+            // decode
+            val inputStream = ByteArrayInputStream(outputStream.toByteArray())
+            val result = codec.decode(inputStream).use { it.readBytes() }
+
+            assertWithMessage("$size bytes: $data").that(result).isEqualTo(data)
+        }
+
+        for (codec in allCodecs()) {
+            test(codec, 0)
+            repeat(10) { test(codec, random.nextInt(1, 1024)) }
+        }
+    }
+}
diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreContextTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreContextTest.kt
new file mode 100644
index 0000000..911665a
--- /dev/null
+++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreContextTest.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.settingslib.datastore
+
+import android.app.backup.BackupDataOutput
+import android.os.Build
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+
+/** Tests of [BackupContext] and [RestoreContext]. */
+@RunWith(AndroidJUnit4::class)
+class BackupRestoreContextTest {
+    @Test
+    fun backupContext_quota() {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
+        val data = mock<BackupDataOutput> { on { quota } doReturn 10L }
+        assertThat(BackupContext(data).quota).isEqualTo(10)
+    }
+
+    @Test
+    fun backupContext_transportFlags() {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) return
+        val data = mock<BackupDataOutput> { on { transportFlags } doReturn 5 }
+        assertThat(BackupContext(data).transportFlags).isEqualTo(5)
+    }
+}
diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreFileArchiverTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreFileArchiverTest.kt
new file mode 100644
index 0000000..6cce453
--- /dev/null
+++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreFileArchiverTest.kt
@@ -0,0 +1,275 @@
+/*
+ * 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.settingslib.datastore
+
+import android.app.Application
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import java.io.ByteArrayInputStream
+import java.io.ByteArrayOutputStream
+import java.io.File
+import java.io.IOException
+import java.io.InputStream
+import kotlin.random.Random
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+
+/** Tests of [BackupRestoreFileArchiver]. */
+@RunWith(AndroidJUnit4::class)
+class BackupRestoreFileArchiverTest {
+    private val random = Random.Default
+    private val application: Application = getApplicationContext()
+    @get:Rule val temporaryFolder = TemporaryFolder(application.dataDirCompat)
+
+    @Test
+    fun createBackupRestoreEntities() {
+        val fileStorages = mutableListOf<BackupRestoreFileStorage>()
+        for (count in 0 until 3) {
+            val fileArchiver = BackupRestoreFileArchiver(application, fileStorages, "")
+            fileArchiver.createBackupRestoreEntities().apply {
+                assertThat(this).hasSize(fileStorages.size)
+                for (index in 0 until count) {
+                    assertThat(get(index).key).isEqualTo(fileStorages[index].storageFilePath)
+                }
+            }
+            fileStorages.add(FileStorage("storage", "path$count"))
+        }
+    }
+
+    @Test
+    fun wrapBackupOutputStream() {
+        val fileArchiver = BackupRestoreFileArchiver(application, listOf(), "")
+        val outputStream = ByteArrayOutputStream()
+        assertThat(fileArchiver.wrapBackupOutputStream(BackupZipCodec.BEST_SPEED, outputStream))
+            .isSameInstanceAs(outputStream)
+    }
+
+    @Test
+    fun wrapRestoreInputStream() {
+        val fileArchiver = BackupRestoreFileArchiver(application, listOf(), "")
+        val inputStream = ByteArrayInputStream(byteArrayOf())
+        assertThat(fileArchiver.wrapRestoreInputStream(BackupZipCodec.BEST_SPEED, inputStream))
+            .isSameInstanceAs(inputStream)
+    }
+
+    @Test
+    fun restoreEntity_disabled() {
+        val file = temporaryFolder.newFile()
+        val key = file.name
+        val fileStorage = FileStorage("fs", key, restoreEnabled = false)
+
+        BackupRestoreFileArchiver(application, listOf(fileStorage), "archiver").apply {
+            restoreEntity(newBackupDataInputStream(key, byteArrayOf()))
+            assertThat(entityStates.asMap()).isEmpty()
+        }
+    }
+
+    @Test
+    fun restoreEntity_raiseIOException() {
+        val key = "key"
+        val fileStorage = FileStorage("fs", key)
+        BackupRestoreFileArchiver(application, listOf(fileStorage), "archiver").apply {
+            restoreEntity(newBackupDataInputStream(key, byteArrayOf(), IOException()))
+            assertThat(entityStates.asMap()).isEmpty()
+        }
+    }
+
+    @Test
+    fun restoreEntity_onRestoreFinished_raiseException() {
+        val key = "key"
+        val fileStorage = FileStorage("fs", key, restoreException = IllegalStateException())
+        BackupRestoreFileArchiver(application, listOf(fileStorage), "archiver").apply {
+            val data = random.nextBytes(random.nextInt(10))
+            val outputStream = ByteArrayOutputStream()
+            fileStorage.wrapBackupOutputStream(fileStorage.defaultCodec(), outputStream).use {
+                it.write(data)
+            }
+            val payload = outputStream.toByteArray()
+            restoreEntity(newBackupDataInputStream(key, payload))
+            assertThat(entityStates.asMap()).isEmpty()
+        }
+    }
+
+    @Test
+    fun restoreEntity_forwardCompatibility() {
+        val key = "key"
+        val fileStorage = FileStorage("fs", key)
+        for (codec in allCodecs()) {
+            BackupRestoreFileArchiver(application, listOf(), "archiver").apply {
+                val data = random.nextBytes(random.nextInt(MAX_DATA_SIZE))
+                val outputStream = ByteArrayOutputStream()
+                fileStorage.wrapBackupOutputStream(codec, outputStream).use { it.write(data) }
+                val payload = outputStream.toByteArray()
+
+                restoreEntity(newBackupDataInputStream(key, payload))
+
+                assertThat(entityStates.asMap()).apply {
+                    hasSize(1)
+                    containsKey(key)
+                }
+                assertThat(fileStorage.restoreFile.readBytes()).isEqualTo(data)
+            }
+        }
+    }
+
+    @Test
+    fun restoreEntity() {
+        val folder = File(application.dataDirCompat, "backup")
+        val file = File(folder, "file")
+        val key = "${folder.name}${File.separator}${file.name}"
+        fun test(codec: BackupCodec, size: Int) {
+            val fileStorage = FileStorage("fs", key, if (size % 2 == 0) codec else null)
+            val data = random.nextBytes(size)
+            val outputStream = ByteArrayOutputStream()
+            fileStorage.wrapBackupOutputStream(codec, outputStream).use { it.write(data) }
+            val payload = outputStream.toByteArray()
+
+            val fileArchiver =
+                BackupRestoreFileArchiver(application, listOf(fileStorage), "archiver")
+            fileArchiver.restoreEntity(newBackupDataInputStream(key, payload))
+
+            assertThat(fileArchiver.entityStates.asMap()).apply {
+                hasSize(1)
+                containsKey(key)
+            }
+            assertThat(file.readBytes()).isEqualTo(data)
+        }
+
+        for (codec in allCodecs()) {
+            for (size in 0 until 100) test(codec, size)
+            repeat(10) { test(codec, random.nextInt(100, MAX_DATA_SIZE)) }
+        }
+    }
+
+    @Test
+    fun onRestoreFinished() {
+        val fileStorage = mock<BackupRestoreFileStorage>()
+        val fileArchiver = BackupRestoreFileArchiver(application, listOf(fileStorage), "")
+
+        fileArchiver.onRestoreFinished()
+
+        verify(fileStorage).onRestoreFinished()
+    }
+
+    @Test
+    fun toBackupRestoreEntity_backup_disabled() {
+        val context = BackupContext(mock())
+        val fileStorage =
+            mock<BackupRestoreFileStorage> { on { enableBackup(context) } doReturn false }
+
+        assertThat(fileStorage.toBackupRestoreEntity().backup(context, ByteArrayOutputStream()))
+            .isEqualTo(EntityBackupResult.INTACT)
+
+        verify(fileStorage, never()).prepareBackup(any())
+    }
+
+    @Test
+    fun toBackupRestoreEntity_backup_fileNotExist() {
+        val context = BackupContext(mock())
+        val file = File("NotExist")
+        val fileStorage =
+            mock<BackupRestoreFileStorage> {
+                on { enableBackup(context) } doReturn true
+                on { backupFile } doReturn file
+            }
+
+        assertThat(fileStorage.toBackupRestoreEntity().backup(context, ByteArrayOutputStream()))
+            .isEqualTo(EntityBackupResult.DELETE)
+
+        verify(fileStorage).prepareBackup(file)
+        verify(fileStorage, never()).defaultCodec()
+    }
+
+    @Test
+    fun toBackupRestoreEntity_backup() {
+        val context = BackupContext(mock())
+        val file = temporaryFolder.newFile()
+
+        fun test(codec: BackupCodec, size: Int) {
+            val data = random.nextBytes(size)
+            file.outputStream().use { it.write(data) }
+
+            val outputStream = ByteArrayOutputStream()
+            val fileStorage =
+                mock<BackupRestoreFileStorage> {
+                    on { enableBackup(context) } doReturn true
+                    on { backupFile } doReturn file
+                    on { defaultCodec() } doReturn codec
+                    on { wrapBackupOutputStream(any(), any()) }.thenCallRealMethod()
+                    on { wrapRestoreInputStream(any(), any()) }.thenCallRealMethod()
+                    on { prepareBackup(any()) }.thenCallRealMethod()
+                    on { onBackupFinished(any()) }.thenCallRealMethod()
+                }
+
+            assertThat(fileStorage.toBackupRestoreEntity().backup(context, outputStream))
+                .isEqualTo(EntityBackupResult.UPDATE)
+
+            verify(fileStorage).prepareBackup(file)
+            verify(fileStorage).onBackupFinished(file)
+
+            val decodedData =
+                fileStorage
+                    .wrapRestoreInputStream(codec, ByteArrayInputStream(outputStream.toByteArray()))
+                    .readBytes()
+            assertThat(decodedData).isEqualTo(data)
+        }
+
+        for (codec in allCodecs()) {
+            // test small data to ensure correctness
+            for (size in 0 until 100) test(codec, size)
+            repeat(10) { test(codec, random.nextInt(100, MAX_DATA_SIZE)) }
+        }
+    }
+
+    @Test
+    fun toBackupRestoreEntity_restore() {
+        val restoreContext = RestoreContext("storage")
+        val inputStream =
+            object : InputStream() {
+                override fun read() = throw IllegalStateException()
+
+                override fun read(b: ByteArray, off: Int, len: Int) = throw IllegalStateException()
+            }
+        FileStorage("storage", "path").toBackupRestoreEntity().restore(restoreContext, inputStream)
+    }
+
+    private open class FileStorage(
+        override val name: String,
+        filePath: String,
+        private val codec: BackupCodec? = null,
+        private val restoreEnabled: Boolean? = null,
+        private val restoreException: Exception? = null,
+    ) : BackupRestoreFileStorage(getApplicationContext(), filePath) {
+
+        override fun defaultCodec() = codec ?: super.defaultCodec()
+
+        override fun enableRestore() = restoreEnabled ?: super.enableRestore()
+
+        override fun onRestoreFinished(file: File) {
+            super.onRestoreFinished(file)
+            if (restoreException != null) throw restoreException
+        }
+    }
+}
diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreFileStorageTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreFileStorageTest.kt
new file mode 100644
index 0000000..422273d
--- /dev/null
+++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreFileStorageTest.kt
@@ -0,0 +1,105 @@
+/*
+ * 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.settingslib.datastore
+
+import android.app.Application
+import android.os.Build
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import java.io.File
+import kotlin.test.assertFailsWith
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** Tests of [BackupRestoreFileStorage]. */
+@RunWith(AndroidJUnit4::class)
+class BackupRestoreFileStorageTest {
+    private val application: Application = getApplicationContext()
+
+    @Test
+    fun dataDirCompat() {
+        val expected =
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+                application.dataDir
+            } else {
+                File(application.applicationInfo.dataDir)
+            }
+        assertThat(application.dataDirCompat).isEqualTo(expected)
+    }
+
+    @Test
+    fun backupFile() {
+        assertThat(FileStorage("path").backupFile.toString())
+            .startsWith(application.dataDirCompat.toString())
+    }
+
+    @Test
+    fun restoreFile() {
+        FileStorage("path").apply { assertThat(restoreFile).isEqualTo(backupFile) }
+    }
+
+    @Test
+    fun checkFilePaths() {
+        FileStorage("path").checkFilePaths()
+    }
+
+    @Test
+    fun checkFilePaths_emptyFilePath() {
+        assertFailsWith(IllegalArgumentException::class) { FileStorage("").checkFilePaths() }
+    }
+
+    @Test
+    fun checkFilePaths_absoluteFilePath() {
+        assertFailsWith(IllegalArgumentException::class) {
+            FileStorage("${File.separatorChar}file").checkFilePaths()
+        }
+    }
+
+    @Test
+    fun checkFilePaths_backupFile() {
+        assertFailsWith(IllegalArgumentException::class) {
+            FileStorage("path", fileForBackup = File("path")).checkFilePaths()
+        }
+    }
+
+    @Test
+    fun checkFilePaths_restoreFile() {
+        assertFailsWith(IllegalArgumentException::class) {
+            FileStorage("path", fileForRestore = File("path")).checkFilePaths()
+        }
+    }
+
+    @Test
+    fun createBackupRestoreEntities() {
+        assertThat(FileStorage("path").createBackupRestoreEntities()).isEmpty()
+    }
+
+    private class FileStorage(
+        filePath: String,
+        val fileForBackup: File? = null,
+        val fileForRestore: File? = null,
+    ) : BackupRestoreFileStorage(getApplicationContext(), filePath) {
+        override val name = "storage"
+
+        override val backupFile: File
+            get() = fileForBackup ?: super.backupFile
+
+        override val restoreFile: File
+            get() = fileForRestore ?: super.restoreFile
+    }
+}
diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageManagerTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageManagerTest.kt
new file mode 100644
index 0000000..d8f5028
--- /dev/null
+++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageManagerTest.kt
@@ -0,0 +1,237 @@
+/*
+ * 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.settingslib.datastore
+
+import android.app.Application
+import android.app.backup.BackupAgentHelper
+import android.app.backup.BackupHelper
+import android.app.backup.BackupManager
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import com.google.common.util.concurrent.MoreExecutors.directExecutor
+import kotlin.test.assertFailsWith
+import org.junit.After
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.reset
+import org.mockito.kotlin.verify
+import org.robolectric.Shadows
+import org.robolectric.shadows.ShadowBackupManager
+
+/** Tests of [BackupRestoreStorageManager]. */
+@RunWith(AndroidJUnit4::class)
+class BackupRestoreStorageManagerTest {
+    private val application: Application = getApplicationContext()
+    private val manager = BackupRestoreStorageManager.getInstance(application)
+    private val fileStorage = FileStorage("fileStorage")
+    private val keyedStorage = KeyedStorage("keyedStorage")
+
+    private val storage1 = mock<ObservableBackupRestoreStorage> { on { name } doReturn "1" }
+    private val storage2 = mock<ObservableBackupRestoreStorage> { on { name } doReturn "1" }
+
+    @After
+    fun tearDown() {
+        manager.removeAll()
+        ShadowBackupManager.reset()
+    }
+
+    @Test
+    fun getInstance() {
+        assertThat(BackupRestoreStorageManager.getInstance(application)).isSameInstanceAs(manager)
+    }
+
+    @Test
+    fun addBackupAgentHelpers() {
+        val fs = FileStorage("fs")
+        manager.add(keyedStorage, fileStorage, storage1, fs)
+        val backupAgentHelper = DummyBackupAgentHelper()
+        manager.addBackupAgentHelpers(backupAgentHelper)
+        backupAgentHelper.backupHelpers.apply {
+            assertThat(size).isEqualTo(3)
+            assertThat(remove(keyedStorage.name)).isSameInstanceAs(keyedStorage)
+            assertThat(remove(storage1.name)).isSameInstanceAs(storage1)
+            val fileArchiver = entries.first().value as BackupRestoreFileArchiver
+            assertThat(fileArchiver.fileStorages.toSet()).containsExactly(fs, fileStorage)
+        }
+    }
+
+    @Test
+    fun addBackupAgentHelpers_withoutFileStorage() {
+        manager.add(keyedStorage, storage1)
+        val backupAgentHelper = DummyBackupAgentHelper()
+        manager.addBackupAgentHelpers(backupAgentHelper)
+        backupAgentHelper.backupHelpers.apply {
+            assertThat(size).isEqualTo(3)
+            assertThat(remove(keyedStorage.name)).isSameInstanceAs(keyedStorage)
+            assertThat(remove(storage1.name)).isSameInstanceAs(storage1)
+            val fileArchiver = entries.first().value as BackupRestoreFileArchiver
+            assertThat(fileArchiver.fileStorages).isEmpty()
+        }
+    }
+
+    @Test
+    fun add() {
+        manager.add(keyedStorage, fileStorage, storage1)
+        assertThat(manager.storageWrappers).apply {
+            hasSize(3)
+            containsKey(keyedStorage.name)
+            containsKey(fileStorage.name)
+            containsKey(storage1.name)
+        }
+    }
+
+    @Test
+    fun add_identicalName() {
+        manager.add(storage1)
+        assertFailsWith(IllegalStateException::class) { manager.add(storage1) }
+        assertFailsWith(IllegalStateException::class) { manager.add(storage2) }
+    }
+
+    @Test
+    fun add_nonObservable() {
+        assertFailsWith(IllegalArgumentException::class) {
+            manager.add(mock<BackupRestoreStorage>())
+        }
+    }
+
+    @Test
+    fun removeAll() {
+        add()
+        manager.removeAll()
+        assertThat(manager.storageWrappers).isEmpty()
+    }
+
+    @Test
+    fun remove() {
+        manager.add(keyedStorage, fileStorage)
+        assertThat(manager.remove(storage1.name)).isNull()
+        assertThat(manager.remove(keyedStorage.name)).isSameInstanceAs(keyedStorage)
+        assertThat(manager.remove(fileStorage.name)).isSameInstanceAs(fileStorage)
+    }
+
+    @Test
+    fun get() {
+        manager.add(keyedStorage, fileStorage)
+        assertThat(manager.get(storage1.name)).isNull()
+        assertThat(manager.get(keyedStorage.name)).isSameInstanceAs(keyedStorage)
+        assertThat(manager.get(fileStorage.name)).isSameInstanceAs(fileStorage)
+    }
+
+    @Test
+    fun getOrThrow() {
+        manager.add(keyedStorage, fileStorage)
+        assertFailsWith(NullPointerException::class) { manager.getOrThrow(storage1.name) }
+        assertThat(manager.getOrThrow(keyedStorage.name)).isSameInstanceAs(keyedStorage)
+        assertThat(manager.getOrThrow(fileStorage.name)).isSameInstanceAs(fileStorage)
+    }
+
+    @Test
+    fun notifyRestoreFinished() {
+        manager.add(keyedStorage, fileStorage)
+        val keyedObserver = mock<KeyedObserver<String>>()
+        val anyKeyObserver = mock<KeyedObserver<String?>>()
+        val observer = mock<Observer>()
+        val executor = directExecutor()
+        keyedStorage.addObserver("key", keyedObserver, executor)
+        keyedStorage.addObserver(anyKeyObserver, executor)
+        fileStorage.addObserver(observer, executor)
+
+        manager.onRestoreFinished()
+
+        verify(keyedObserver).onKeyChanged("key", ChangeReason.RESTORE)
+        verify(anyKeyObserver).onKeyChanged(null, ChangeReason.RESTORE)
+        verify(observer).onChanged(ChangeReason.RESTORE)
+        if (isRobolectric()) {
+            Shadows.shadowOf(BackupManager(application)).apply {
+                assertThat(isDataChanged).isFalse()
+                assertThat(dataChangedCount).isEqualTo(0)
+            }
+        }
+    }
+
+    @Test
+    fun notifyBackupManager() {
+        manager.add(keyedStorage, fileStorage)
+        val keyedObserver = mock<KeyedObserver<String>>()
+        val anyKeyObserver = mock<KeyedObserver<String?>>()
+        val observer = mock<Observer>()
+        val executor = directExecutor()
+        keyedStorage.addObserver("key", keyedObserver, executor)
+        keyedStorage.addObserver(anyKeyObserver, executor)
+        fileStorage.addObserver(observer, executor)
+
+        val backupManager =
+            if (isRobolectric()) Shadows.shadowOf(BackupManager(application)) else null
+        backupManager?.apply {
+            assertThat(isDataChanged).isFalse()
+            assertThat(dataChangedCount).isEqualTo(0)
+        }
+
+        fileStorage.notifyChange(ChangeReason.UPDATE)
+        verify(observer).onChanged(ChangeReason.UPDATE)
+        verify(keyedObserver, never()).onKeyChanged(any(), any())
+        verify(anyKeyObserver, never()).onKeyChanged(any(), any())
+        reset(observer)
+        backupManager?.apply {
+            assertThat(isDataChanged).isTrue()
+            assertThat(dataChangedCount).isEqualTo(1)
+        }
+
+        keyedStorage.notifyChange("key", ChangeReason.DELETE)
+        verify(observer, never()).onChanged(any())
+        verify(keyedObserver).onKeyChanged("key", ChangeReason.DELETE)
+        verify(anyKeyObserver).onKeyChanged("key", ChangeReason.DELETE)
+        backupManager?.apply {
+            assertThat(isDataChanged).isTrue()
+            assertThat(dataChangedCount).isEqualTo(2)
+        }
+        reset(keyedObserver)
+
+        // backup manager is not notified for restore event
+        fileStorage.notifyChange(ChangeReason.RESTORE)
+        keyedStorage.notifyChange("key", ChangeReason.RESTORE)
+        verify(observer).onChanged(ChangeReason.RESTORE)
+        verify(keyedObserver).onKeyChanged("key", ChangeReason.RESTORE)
+        verify(anyKeyObserver).onKeyChanged("key", ChangeReason.RESTORE)
+        backupManager?.apply {
+            assertThat(isDataChanged).isTrue()
+            assertThat(dataChangedCount).isEqualTo(2)
+        }
+    }
+
+    private class KeyedStorage(override val name: String) :
+        BackupRestoreStorage(), KeyedObservable<String> by KeyedDataObservable() {
+
+        override fun createBackupRestoreEntities(): List<BackupRestoreEntity> = listOf()
+    }
+
+    private class FileStorage(override val name: String) :
+        BackupRestoreFileStorage(getApplicationContext(), "file"), Observable by DataObservable()
+
+    private class DummyBackupAgentHelper : BackupAgentHelper() {
+        val backupHelpers = mutableMapOf<String, BackupHelper>()
+
+        override fun addHelper(keyPrefix: String, helper: BackupHelper) {
+            backupHelpers[keyPrefix] = helper
+        }
+    }
+}
diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageTest.kt
new file mode 100644
index 0000000..99998ff
--- /dev/null
+++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageTest.kt
@@ -0,0 +1,414 @@
+/*
+ * 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.settingslib.datastore
+
+import android.app.backup.BackupAgentHelper
+import android.app.backup.BackupDataInput
+import android.app.backup.BackupDataInputStream
+import android.app.backup.BackupDataOutput
+import android.os.ParcelFileDescriptor
+import android.os.ParcelFileDescriptor.MODE_APPEND
+import android.os.ParcelFileDescriptor.MODE_READ_ONLY
+import android.os.ParcelFileDescriptor.MODE_WRITE_ONLY
+import androidx.collection.MutableScatterMap
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import java.io.DataOutputStream
+import java.io.File
+import java.io.FileDescriptor
+import java.io.FileOutputStream
+import java.io.InputStream
+import java.io.OutputStream
+import kotlin.random.Random
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.doThrow
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.reset
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.verify
+
+/** Tests of [BackupRestoreStorage]. */
+@RunWith(AndroidJUnit4::class)
+class BackupRestoreStorageTest {
+    @get:Rule val temporaryFolder = TemporaryFolder()
+
+    private val entity1 = Entity("key1", "value1".toByteArray())
+    private val entity1NoOpCodec = Entity("key1", "value1".toByteArray(), BackupNoOpCodec())
+    private val entity2 = Entity("key2", "value2".toByteArray(), BackupZipCodec.BEST_SPEED)
+
+    @Test
+    fun performBackup_disabled() {
+        val storage = spy(TestStorage().apply { enabled = false })
+        val unused = performBackup { data, newState -> storage.performBackup(null, data, newState) }
+        verify(storage, never()).createBackupRestoreEntities()
+        assertThat(storage.entities).isNull()
+        assertThat(storage.entityStates.size).isEqualTo(0)
+    }
+
+    @Test
+    fun performBackup_enabled() {
+        val storage = spy(TestStorage())
+        val unused = performBackup { data, newState -> storage.performBackup(null, data, newState) }
+        verify(storage).createBackupRestoreEntities()
+        assertThat(storage.entities).isNull()
+        assertThat(storage.entityStates.size).isEqualTo(0)
+    }
+
+    @Test
+    fun performBackup_entityBackupWithException() {
+        val entity =
+            mock<BackupRestoreEntity> {
+                on { key } doReturn ""
+                on { backup(any(), any()) } doThrow IllegalStateException()
+            }
+        val storage = TestStorage(entity, entity1)
+
+        val (_, stateFile) =
+            performBackup { data, newState -> storage.performBackup(null, data, newState) }
+
+        assertThat(storage.readEntityStates(stateFile)).apply {
+            hasSize(1)
+            containsKey(entity1.key)
+        }
+    }
+
+    @Test
+    fun performBackup_update_unchanged() {
+        performBackupTest({}) { entityStates, newEntityStates ->
+            assertThat(entityStates).isEqualTo(newEntityStates)
+        }
+    }
+
+    @Test
+    fun performBackup_intact() {
+        performBackupTest({ entity1.backupResult = EntityBackupResult.INTACT }) {
+            entityStates,
+            newEntityStates ->
+            assertThat(entityStates).isEqualTo(newEntityStates)
+        }
+    }
+
+    @Test
+    fun performBackup_delete() {
+        performBackupTest({ entity1.backupResult = EntityBackupResult.DELETE }) { _, newEntityStates
+            ->
+            assertThat(newEntityStates.size).isEqualTo(1)
+            assertThat(newEntityStates).containsKey(entity2.key)
+        }
+    }
+
+    private fun performBackupTest(
+        update: () -> Unit,
+        verification: (Map<String, Long>, Map<String, Long>) -> Unit,
+    ) {
+        val storage = TestStorage(entity1, entity2)
+        val (_, stateFile) =
+            performBackup { data, newState -> storage.performBackup(null, data, newState) }
+
+        val entityStates = storage.readEntityStates(stateFile)
+        assertThat(entityStates).apply {
+            hasSize(2)
+            containsKey(entity1.key)
+            containsKey(entity2.key)
+        }
+
+        update.invoke()
+        val (_, newStateFile) =
+            performBackup { data, newState ->
+                stateFile.toParcelFileDescriptor(MODE_READ_ONLY).use {
+                    storage.performBackup(it, data, newState)
+                }
+            }
+        verification.invoke(entityStates, storage.readEntityStates(newStateFile))
+    }
+
+    @Test
+    fun restoreEntity_disabled() {
+        val storage = spy(TestStorage().apply { enabled = false })
+        temporaryFolder.newFile().toParcelFileDescriptor(MODE_READ_ONLY).use {
+            storage.restoreEntity(it.toBackupDataInputStream())
+        }
+        verify(storage, never()).createBackupRestoreEntities()
+        assertThat(storage.entities).isNull()
+        assertThat(storage.entityStates.size).isEqualTo(0)
+    }
+
+    @Test
+    fun restoreEntity_entityNotFound() {
+        val storage = TestStorage()
+        temporaryFolder.newFile().toParcelFileDescriptor(MODE_READ_ONLY).use {
+            val backupDataInputStream = it.toBackupDataInputStream()
+            backupDataInputStream.setKey("")
+            storage.restoreEntity(backupDataInputStream)
+        }
+    }
+
+    @Test
+    fun restoreEntity_exception() {
+        val storage = TestStorage(entity1)
+        temporaryFolder.newFile().toParcelFileDescriptor(MODE_READ_ONLY).use {
+            val backupDataInputStream = it.toBackupDataInputStream()
+            backupDataInputStream.setKey(entity1.key)
+            storage.restoreEntity(backupDataInputStream)
+        }
+    }
+
+    @Test
+    fun restoreEntity_codecChanged() {
+        assertThat(entity1.codec()).isNotEqualTo(entity1NoOpCodec.codec())
+        backupAndRestore(entity1) { _, data ->
+            TestStorage(entity1NoOpCodec).apply { restoreEntity(data) }
+        }
+        assertThat(entity1.data).isEqualTo(entity1NoOpCodec.restoredData)
+    }
+
+    @Test
+    fun restoreEntity() {
+        val random = Random.Default
+        fun test(codec: BackupCodec, size: Int) {
+            val entity = Entity("key", random.nextBytes(size), codec)
+            backupAndRestore(entity)
+            entity.verifyRestoredData()
+        }
+        for (codec in allCodecs()) {
+            // test small data to ensure correctness
+            for (size in 0 until 100) test(codec, size)
+            repeat(10) { test(codec, random.nextInt(100, MAX_DATA_SIZE)) }
+        }
+    }
+
+    @Test
+    fun readEntityStates_eof_exception() {
+        val storage = TestStorage()
+        val entityStates = MutableScatterMap<String, Long>()
+        entityStates.put("", 0) // add an item to verify that exiting elements are clear
+        temporaryFolder.newFile().toParcelFileDescriptor(MODE_READ_ONLY).use {
+            storage.readEntityStates(it, entityStates)
+        }
+        assertThat(entityStates.size).isEqualTo(0)
+    }
+
+    @Test
+    fun readEntityStates_other_exception() {
+        val storage = TestStorage()
+        val entityStates = MutableScatterMap<String, Long>()
+        entityStates.put("", 0) // add an item to verify that exiting elements are clear
+        temporaryFolder.newFile().toParcelFileDescriptor(MODE_READ_ONLY).apply {
+            close() // cause exception when read state file
+            storage.readEntityStates(this, entityStates)
+        }
+        assertThat(entityStates.size).isEqualTo(0)
+    }
+
+    @Test
+    fun readEntityStates_unknownVersion() {
+        val storage = TestStorage()
+        val stateFile = temporaryFolder.newFile()
+        stateFile.toParcelFileDescriptor(MODE_WRITE_ONLY or MODE_APPEND).use {
+            DataOutputStream(FileOutputStream(it.fileDescriptor))
+                .writeByte(BackupRestoreStorage.STATE_VERSION + 1)
+        }
+        val entityStates = MutableScatterMap<String, Long>()
+        entityStates.put("", 0) // add an item to verify that exiting elements are clear
+        stateFile.toParcelFileDescriptor(MODE_READ_ONLY).use {
+            storage.readEntityStates(it, entityStates)
+        }
+        assertThat(entityStates.size).isEqualTo(0)
+    }
+
+    @Test
+    fun writeNewStateDescription() {
+        val storage = spy(TestStorage())
+        // use read only mode to trigger exception when write state file
+        temporaryFolder.newFile().toParcelFileDescriptor(MODE_READ_ONLY).use {
+            storage.writeNewStateDescription(it)
+        }
+        verify(storage).onRestoreFinished()
+    }
+
+    @Test
+    fun backupAndRestore() {
+        val storage = spy(TestStorage(entity1, entity2))
+        val backupAgentHelper = BackupAgentHelper()
+        backupAgentHelper.addHelper(storage.name, storage)
+
+        // backup
+        val (dataFile, stateFile) =
+            performBackup { data, newState -> backupAgentHelper.onBackup(null, data, newState) }
+        storage.verifyFieldsArePurged()
+
+        // verify state
+        val entityStates = MutableScatterMap<String, Long>()
+        entityStates[""] = 1
+        storage.readEntityStates(null, entityStates)
+        assertThat(entityStates.size).isEqualTo(0)
+        stateFile.toParcelFileDescriptor(MODE_READ_ONLY).use {
+            storage.readEntityStates(it, entityStates)
+        }
+        assertThat(entityStates.asMap()).apply {
+            hasSize(2)
+            containsKey(entity1.key)
+            containsKey(entity2.key)
+        }
+        reset(storage)
+
+        // restore
+        val newStateFile = temporaryFolder.newFile()
+        dataFile.toParcelFileDescriptor(MODE_READ_ONLY).use { dataPfd ->
+            newStateFile.toParcelFileDescriptor(MODE_WRITE_ONLY or MODE_APPEND).use {
+                backupAgentHelper.onRestore(dataPfd.toBackupDataInput(), 0, it)
+            }
+        }
+        verify(storage).onRestoreFinished()
+        storage.verifyFieldsArePurged()
+
+        // ShadowBackupDataOutput does not write data to file, so restore is bypassed
+        if (!isRobolectric()) {
+            entity1.verifyRestoredData()
+            entity2.verifyRestoredData()
+            assertThat(entityStates.asMap()).isEqualTo(storage.readEntityStates(newStateFile))
+        }
+    }
+
+    private fun backupAndRestore(
+        entity: BackupRestoreEntity,
+        restoreEntity: (TestStorage, BackupDataInputStream) -> TestStorage = { storage, data ->
+            storage.restoreEntity(data)
+            storage
+        },
+    ) {
+        val storage = TestStorage(entity)
+        val entityKey = argumentCaptor<String>()
+        val entitySize = argumentCaptor<Int>()
+        val entityData = argumentCaptor<ByteArray>()
+        val data = mock<BackupDataOutput>()
+
+        val stateFile = temporaryFolder.newFile()
+        stateFile.toParcelFileDescriptor(MODE_WRITE_ONLY or MODE_APPEND).use {
+            storage.performBackup(null, data, it)
+        }
+        val entityStates = MutableScatterMap<String, Long>()
+        stateFile.toParcelFileDescriptor(MODE_READ_ONLY).use {
+            storage.readEntityStates(it, entityStates)
+        }
+        assertThat(entityStates.size).isEqualTo(1)
+
+        verify(data).writeEntityHeader(entityKey.capture(), entitySize.capture())
+        verify(data).writeEntityData(entityData.capture(), entitySize.capture())
+        assertThat(entityKey.allValues).isEqualTo(listOf(entity.key))
+        assertThat(entityData.allValues).hasSize(1)
+        val payload = entityData.firstValue
+        assertThat(entitySize.allValues).isEqualTo(listOf(payload.size, payload.size))
+
+        val dataFile = temporaryFolder.newFile()
+        dataFile.toParcelFileDescriptor(MODE_WRITE_ONLY or MODE_APPEND).use {
+            FileOutputStream(it.fileDescriptor).write(payload)
+        }
+
+        newBackupDataInputStream(entity.key, payload).apply {
+            restoreEntity.invoke(storage, this).also {
+                assertThat(it.entityStates).isEqualTo(entityStates)
+            }
+        }
+    }
+
+    fun performBackup(backup: (BackupDataOutput, ParcelFileDescriptor) -> Unit): Pair<File, File> {
+        val dataFile = temporaryFolder.newFile()
+        val stateFile = temporaryFolder.newFile()
+        dataFile.toParcelFileDescriptor(MODE_WRITE_ONLY or MODE_APPEND).use { dataPfd ->
+            stateFile.toParcelFileDescriptor(MODE_WRITE_ONLY or MODE_APPEND).use {
+                backup.invoke(dataPfd.toBackupDataOutput(), it)
+            }
+        }
+        return dataFile to stateFile
+    }
+
+    private fun BackupRestoreStorage.verifyFieldsArePurged() {
+        assertThat(entities).isNull()
+        assertThat(entityStates.size).isEqualTo(0)
+        assertThat(entityStates.capacity).isEqualTo(0)
+    }
+
+    private fun BackupRestoreStorage.readEntityStates(stateFile: File): Map<String, Long> {
+        val entityStates = MutableScatterMap<String, Long>()
+        stateFile.toParcelFileDescriptor(MODE_READ_ONLY).use { readEntityStates(it, entityStates) }
+        return entityStates.asMap()
+    }
+
+    private fun File.toParcelFileDescriptor(mode: Int) = ParcelFileDescriptor.open(this, mode)
+
+    private fun ParcelFileDescriptor.toBackupDataOutput() = fileDescriptor.toBackupDataOutput()
+
+    private fun ParcelFileDescriptor.toBackupDataInputStream(): BackupDataInputStream =
+        BackupDataInputStream::class.java.newInstance(toBackupDataInput())
+
+    private fun ParcelFileDescriptor.toBackupDataInput() = fileDescriptor.toBackupDataInput()
+
+    private fun FileDescriptor.toBackupDataOutput(): BackupDataOutput =
+        BackupDataOutput::class.java.newInstance(this)
+
+    private fun FileDescriptor.toBackupDataInput(): BackupDataInput =
+        BackupDataInput::class.java.newInstance(this)
+}
+
+private open class TestStorage(vararg val backupRestoreEntities: BackupRestoreEntity) :
+    ObservableBackupRestoreStorage() {
+    var enabled: Boolean? = null
+
+    override val name
+        get() = "TestBackup"
+
+    override fun createBackupRestoreEntities() = backupRestoreEntities.toList()
+
+    override fun enableBackup(backupContext: BackupContext) =
+        enabled ?: super.enableBackup(backupContext)
+
+    override fun enableRestore() = enabled ?: super.enableRestore()
+}
+
+private class Entity(
+    override val key: String,
+    val data: ByteArray,
+    private val codec: BackupCodec? = null,
+) : BackupRestoreEntity {
+    var restoredData: ByteArray? = null
+    var backupResult = EntityBackupResult.UPDATE
+
+    override fun codec() = codec ?: super.codec()
+
+    override fun backup(
+        backupContext: BackupContext,
+        outputStream: OutputStream,
+    ): EntityBackupResult {
+        outputStream.write(data)
+        return backupResult
+    }
+
+    override fun restore(restoreContext: RestoreContext, inputStream: InputStream) {
+        restoredData = inputStream.readBytes()
+        inputStream.close()
+    }
+
+    fun verifyRestoredData() = assertThat(restoredData).isEqualTo(data)
+}
diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/KeyedObserverTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/KeyedObserverTest.kt
index b52586c..8638b2f 100644
--- a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/KeyedObserverTest.kt
+++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/KeyedObserverTest.kt
@@ -16,76 +16,58 @@
 
 package com.android.settingslib.datastore
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.google.common.truth.Truth.assertThat
-import com.google.common.util.concurrent.MoreExecutors.directExecutor
+import com.google.common.util.concurrent.MoreExecutors
 import java.util.concurrent.Executor
 import java.util.concurrent.atomic.AtomicInteger
 import org.junit.Assert
-import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.junit.MockitoJUnit
-import org.mockito.junit.MockitoRule
-import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
 import org.mockito.kotlin.never
 import org.mockito.kotlin.reset
 import org.mockito.kotlin.verify
-import org.robolectric.RobolectricTestRunner
 
-@RunWith(RobolectricTestRunner::class)
+@RunWith(AndroidJUnit4::class)
 class KeyedObserverTest {
-    @get:Rule
-    val mockitoRule: MockitoRule = MockitoJUnit.rule()
+    private val observer1 = mock<KeyedObserver<Any?>>()
+    private val observer2 = mock<KeyedObserver<Any?>>()
+    private val keyedObserver1 = mock<KeyedObserver<Any>>()
+    private val keyedObserver2 = mock<KeyedObserver<Any>>()
 
-    @Mock
-    private lateinit var observer1: KeyedObserver<Any?>
+    private val key1 = Object()
+    private val key2 = Object()
 
-    @Mock
-    private lateinit var observer2: KeyedObserver<Any?>
-
-    @Mock
-    private lateinit var keyedObserver1: KeyedObserver<Any>
-
-    @Mock
-    private lateinit var keyedObserver2: KeyedObserver<Any>
-
-    @Mock
-    private lateinit var key1: Any
-
-    @Mock
-    private lateinit var key2: Any
-
-    @Mock
-    private lateinit var executor: Executor
-
+    private val executor1: Executor = MoreExecutors.directExecutor()
+    private val executor2: Executor = MoreExecutors.newDirectExecutorService()
     private val keyedObservable = KeyedDataObservable<Any>()
 
     @Test
     fun addObserver_sameExecutor() {
-        keyedObservable.addObserver(observer1, executor)
-        keyedObservable.addObserver(observer1, executor)
+        keyedObservable.addObserver(observer1, executor1)
+        keyedObservable.addObserver(observer1, executor1)
     }
 
     @Test
     fun addObserver_keyedObserver_sameExecutor() {
-        keyedObservable.addObserver(key1, keyedObserver1, executor)
-        keyedObservable.addObserver(key1, keyedObserver1, executor)
+        keyedObservable.addObserver(key1, keyedObserver1, executor1)
+        keyedObservable.addObserver(key1, keyedObserver1, executor1)
     }
 
     @Test
     fun addObserver_differentExecutor() {
-        keyedObservable.addObserver(observer1, executor)
+        keyedObservable.addObserver(observer1, executor1)
         Assert.assertThrows(IllegalStateException::class.java) {
-            keyedObservable.addObserver(observer1, directExecutor())
+            keyedObservable.addObserver(observer1, executor2)
         }
     }
 
     @Test
     fun addObserver_keyedObserver_differentExecutor() {
-        keyedObservable.addObserver(key1, keyedObserver1, executor)
+        keyedObservable.addObserver(key1, keyedObserver1, executor1)
         Assert.assertThrows(IllegalStateException::class.java) {
-            keyedObservable.addObserver(key1, keyedObserver1, directExecutor())
+            keyedObservable.addObserver(key1, keyedObserver1, executor2)
         }
     }
 
@@ -93,7 +75,7 @@
     fun addObserver_weaklyReferenced() {
         val counter = AtomicInteger()
         var observer: KeyedObserver<Any?>? = KeyedObserver { _, _ -> counter.incrementAndGet() }
-        keyedObservable.addObserver(observer!!, directExecutor())
+        keyedObservable.addObserver(observer!!, executor1)
 
         keyedObservable.notifyChange(ChangeReason.UPDATE)
         assertThat(counter.get()).isEqualTo(1)
@@ -111,7 +93,7 @@
     fun addObserver_keyedObserver_weaklyReferenced() {
         val counter = AtomicInteger()
         var keyObserver: KeyedObserver<Any>? = KeyedObserver { _, _ -> counter.incrementAndGet() }
-        keyedObservable.addObserver(key1, keyObserver!!, directExecutor())
+        keyedObservable.addObserver(key1, keyObserver!!, executor1)
 
         keyedObservable.notifyChange(key1, ChangeReason.UPDATE)
         assertThat(counter.get()).isEqualTo(1)
@@ -127,45 +109,43 @@
 
     @Test
     fun addObserver_notifyObservers_removeObserver() {
-        keyedObservable.addObserver(observer1, directExecutor())
-        keyedObservable.addObserver(observer2, executor)
+        keyedObservable.addObserver(observer1, executor1)
+        keyedObservable.addObserver(observer2, executor2)
 
         keyedObservable.notifyChange(ChangeReason.UPDATE)
         verify(observer1).onKeyChanged(null, ChangeReason.UPDATE)
-        verify(observer2, never()).onKeyChanged(any(), any())
-        verify(executor).execute(any())
+        verify(observer2).onKeyChanged(null, ChangeReason.UPDATE)
 
-        reset(observer1, executor)
+        reset(observer1, observer2)
         keyedObservable.removeObserver(observer2)
 
         keyedObservable.notifyChange(ChangeReason.DELETE)
         verify(observer1).onKeyChanged(null, ChangeReason.DELETE)
-        verify(executor, never()).execute(any())
+        verify(observer2, never()).onKeyChanged(null, ChangeReason.DELETE)
     }
 
     @Test
     fun addObserver_keyedObserver_notifyObservers_removeObserver() {
-        keyedObservable.addObserver(key1, keyedObserver1, directExecutor())
-        keyedObservable.addObserver(key2, keyedObserver2, executor)
+        keyedObservable.addObserver(key1, keyedObserver1, executor1)
+        keyedObservable.addObserver(key2, keyedObserver2, executor2)
 
         keyedObservable.notifyChange(key1, ChangeReason.UPDATE)
         verify(keyedObserver1).onKeyChanged(key1, ChangeReason.UPDATE)
-        verify(keyedObserver2, never()).onKeyChanged(any(), any())
-        verify(executor, never()).execute(any())
+        verify(keyedObserver2, never()).onKeyChanged(key2, ChangeReason.UPDATE)
 
-        reset(keyedObserver1, executor)
-        keyedObservable.removeObserver(key2, keyedObserver2)
+        reset(keyedObserver1, keyedObserver2)
+        keyedObservable.removeObserver(key1, keyedObserver1)
 
         keyedObservable.notifyChange(key1, ChangeReason.DELETE)
-        verify(keyedObserver1).onKeyChanged(key1, ChangeReason.DELETE)
-        verify(executor, never()).execute(any())
+        verify(keyedObserver1, never()).onKeyChanged(key1, ChangeReason.DELETE)
+        verify(keyedObserver2, never()).onKeyChanged(key2, ChangeReason.DELETE)
     }
 
     @Test
     fun notifyChange_addMoreTypeObservers_checkOnKeyChanged() {
-        keyedObservable.addObserver(observer1, directExecutor())
-        keyedObservable.addObserver(key1, keyedObserver1, directExecutor())
-        keyedObservable.addObserver(key2, keyedObserver2, directExecutor())
+        keyedObservable.addObserver(observer1, executor1)
+        keyedObservable.addObserver(key1, keyedObserver1, executor1)
+        keyedObservable.addObserver(key2, keyedObserver2, executor1)
 
         keyedObservable.notifyChange(ChangeReason.UPDATE)
         verify(observer1).onKeyChanged(null, ChangeReason.UPDATE)
@@ -191,10 +171,10 @@
     fun notifyChange_addObserverWithinCallback() {
         // ConcurrentModificationException is raised if it is not implemented correctly
         val observer: KeyedObserver<Any?> = KeyedObserver { _, _ ->
-            keyedObservable.addObserver(observer1, executor)
+            keyedObservable.addObserver(observer1, executor1)
         }
 
-        keyedObservable.addObserver(observer, directExecutor())
+        keyedObservable.addObserver(observer, executor1)
 
         keyedObservable.notifyChange(ChangeReason.UPDATE)
         keyedObservable.removeObserver(observer)
@@ -204,12 +184,12 @@
     fun notifyChange_KeyedObserver_addObserverWithinCallback() {
         // ConcurrentModificationException is raised if it is not implemented correctly
         val keyObserver: KeyedObserver<Any?> = KeyedObserver { _, _ ->
-            keyedObservable.addObserver(key1, keyedObserver1, executor)
+            keyedObservable.addObserver(key1, keyedObserver1, executor1)
         }
 
-        keyedObservable.addObserver(key1, keyObserver, directExecutor())
+        keyedObservable.addObserver(key1, keyObserver, executor1)
 
         keyedObservable.notifyChange(key1, ChangeReason.UPDATE)
         keyedObservable.removeObserver(key1, keyObserver)
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt
index f065829..173c2b1 100644
--- a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt
+++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt
@@ -22,40 +22,33 @@
 import java.util.concurrent.Executor
 import java.util.concurrent.atomic.AtomicInteger
 import org.junit.Assert.assertThrows
-import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.junit.MockitoJUnit
-import org.mockito.junit.MockitoRule
-import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
 import org.mockito.kotlin.never
 import org.mockito.kotlin.reset
 import org.mockito.kotlin.verify
 
 @RunWith(AndroidJUnit4::class)
 class ObserverTest {
-    @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
+    private val observer1 = mock<Observer>()
+    private val observer2 = mock<Observer>()
 
-    @Mock private lateinit var observer1: Observer
-
-    @Mock private lateinit var observer2: Observer
-
-    @Mock private lateinit var executor: Executor
-
+    private val executor1: Executor = MoreExecutors.directExecutor()
+    private val executor2: Executor = MoreExecutors.newDirectExecutorService()
     private val observable = DataObservable()
 
     @Test
     fun addObserver_sameExecutor() {
-        observable.addObserver(observer1, executor)
-        observable.addObserver(observer1, executor)
+        observable.addObserver(observer1, executor1)
+        observable.addObserver(observer1, executor1)
     }
 
     @Test
     fun addObserver_differentExecutor() {
-        observable.addObserver(observer1, executor)
+        observable.addObserver(observer1, executor1)
         assertThrows(IllegalStateException::class.java) {
-            observable.addObserver(observer1, MoreExecutors.directExecutor())
+            observable.addObserver(observer1, executor2)
         }
     }
 
@@ -63,7 +56,7 @@
     fun addObserver_weaklyReferenced() {
         val counter = AtomicInteger()
         var observer: Observer? = Observer { counter.incrementAndGet() }
-        observable.addObserver(observer!!, MoreExecutors.directExecutor())
+        observable.addObserver(observer!!, executor1)
 
         observable.notifyChange(ChangeReason.UPDATE)
         assertThat(counter.get()).isEqualTo(1)
@@ -79,31 +72,27 @@
 
     @Test
     fun addObserver_notifyObservers_removeObserver() {
-        observable.addObserver(observer1, MoreExecutors.directExecutor())
-        observable.addObserver(observer2, executor)
+        observable.addObserver(observer1, executor1)
+        observable.addObserver(observer2, executor2)
 
         observable.notifyChange(ChangeReason.DELETE)
 
         verify(observer1).onChanged(ChangeReason.DELETE)
-        verify(observer2, never()).onChanged(any())
-        verify(executor).execute(any())
+        verify(observer2).onChanged(ChangeReason.DELETE)
 
-        reset(observer1, executor)
+        reset(observer1, observer2)
         observable.removeObserver(observer2)
 
         observable.notifyChange(ChangeReason.UPDATE)
         verify(observer1).onChanged(ChangeReason.UPDATE)
-        verify(executor, never()).execute(any())
+        verify(observer2, never()).onChanged(ChangeReason.UPDATE)
     }
 
     @Test
     fun notifyChange_addObserverWithinCallback() {
         // ConcurrentModificationException is raised if it is not implemented correctly
-        val observer = Observer { observable.addObserver(observer1, executor) }
-        observable.addObserver(
-            observer,
-            MoreExecutors.directExecutor()
-        )
+        val observer = Observer { observable.addObserver(observer1, executor1) }
+        observable.addObserver(observer, executor1)
         observable.notifyChange(ChangeReason.UPDATE)
         observable.removeObserver(observer)
     }
diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/SharedPreferencesStorageTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/SharedPreferencesStorageTest.kt
new file mode 100644
index 0000000..fec7d75
--- /dev/null
+++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/SharedPreferencesStorageTest.kt
@@ -0,0 +1,175 @@
+/*
+ * 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.settingslib.datastore
+
+import android.app.Application
+import android.content.Context
+import android.content.SharedPreferences
+import android.os.Build
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import com.google.common.truth.Truth.assertThat
+import com.google.common.util.concurrent.MoreExecutors
+import java.io.ByteArrayOutputStream
+import java.io.File
+import java.util.concurrent.Executor
+import kotlin.random.Random
+import org.junit.After
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.verify
+
+/** Tests of [SharedPreferencesStorage]. */
+@RunWith(AndroidJUnit4::class)
+class SharedPreferencesStorageTest {
+    private val random = Random.Default
+    private val application: Application = ApplicationProvider.getApplicationContext()
+    private val map =
+        mapOf(
+            "boolean" to true,
+            "float" to random.nextFloat(),
+            "int" to random.nextInt(),
+            "long" to random.nextLong(),
+            "string" to "string",
+            "set" to setOf("string"),
+        )
+
+    @After
+    fun tearDown() {
+        application.getSharedPreferences(NAME, MODE).edit().clear().applySync()
+    }
+
+    @Test
+    fun constructors() {
+        val storage1 = SharedPreferencesStorage(application, NAME, MODE)
+        val storage2 =
+            SharedPreferencesStorage(
+                application,
+                NAME,
+                application.getSharedPreferences(NAME, MODE),
+            )
+        assertThat(storage1.sharedPreferences).isSameInstanceAs(storage2.sharedPreferences)
+    }
+
+    @Test
+    fun observer() {
+        val observer = mock<KeyedObserver<Any?>>()
+        val keyedObserver = mock<KeyedObserver<Any>>()
+        val storage = SharedPreferencesStorage(application, NAME, MODE)
+        val executor: Executor = MoreExecutors.directExecutor()
+        storage.addObserver(observer, executor)
+        storage.addObserver("key", keyedObserver, executor)
+
+        storage.sharedPreferences.edit().putString("key", "string").applySync()
+        verify(observer).onKeyChanged("key", ChangeReason.UPDATE)
+        verify(keyedObserver).onKeyChanged("key", ChangeReason.UPDATE)
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+            storage.sharedPreferences.edit().clear().applySync()
+            verify(observer).onKeyChanged(null, ChangeReason.DELETE)
+            verify(keyedObserver).onKeyChanged("key", ChangeReason.DELETE)
+        }
+    }
+
+    @Test
+    fun prepareBackup_commitFailed() {
+        val editor = mock<SharedPreferences.Editor> { on { commit() } doReturn false }
+        val storage =
+            spy(SharedPreferencesStorage(application, NAME, MODE)) {
+                onGeneric { mergeSharedPreferences(any(), any(), any()) } doReturn editor
+            }
+        storage.prepareBackup(File(""))
+    }
+
+    @Test
+    fun backupAndRestore() {
+        fun test(codec: BackupCodec) {
+            val storage = SharedPreferencesStorage(application, NAME, MODE, codec)
+            storage.mergeSharedPreferences(storage.sharedPreferences, map, "op").commit()
+            assertThat(storage.sharedPreferences.all).isEqualTo(map)
+
+            val outputStream = ByteArrayOutputStream()
+            assertThat(storage.toBackupRestoreEntity().backup(BackupContext(mock()), outputStream))
+                .isEqualTo(EntityBackupResult.UPDATE)
+            val payload = outputStream.toByteArray()
+
+            storage.sharedPreferences.edit().clear().commit()
+            assertThat(storage.sharedPreferences.all).isEmpty()
+
+            BackupRestoreFileArchiver(application, listOf(storage), "archiver")
+                .restoreEntity(newBackupDataInputStream(storage.storageFilePath, payload))
+            assertThat(storage.sharedPreferences.all).isEqualTo(map)
+        }
+
+        for (codec in allCodecs()) test(codec)
+    }
+
+    @Test
+    fun mergeSharedPreferences_filter() {
+        val storage =
+            SharedPreferencesStorage(application, NAME, MODE) { key, value ->
+                key == "float" || value is String
+            }
+        storage.mergeSharedPreferences(storage.sharedPreferences, map, "op").apply()
+        assertThat(storage.sharedPreferences.all)
+            .containsExactly("float", map["float"], "string", map["string"])
+    }
+
+    @Test
+    fun mergeSharedPreferences_invalidSet() {
+        val storage = SharedPreferencesStorage(application, NAME, MODE, verbose = true)
+        storage
+            .mergeSharedPreferences(
+                storage.sharedPreferences,
+                mapOf<String, Any>("set" to setOf(Any())),
+                "op"
+            )
+            .apply()
+        assertThat(storage.sharedPreferences.all).isEmpty()
+    }
+
+    @Test
+    fun mergeSharedPreferences_unknownType() {
+        val storage = SharedPreferencesStorage(application, NAME, MODE)
+        storage
+            .mergeSharedPreferences(storage.sharedPreferences, map + ("key" to Any()), "op")
+            .apply()
+        assertThat(storage.sharedPreferences.all).isEqualTo(map)
+    }
+
+    @Test
+    fun mergeSharedPreferences() {
+        val storage = SharedPreferencesStorage(application, NAME, MODE, verbose = true)
+        storage.mergeSharedPreferences(storage.sharedPreferences, map, "op").apply()
+        assertThat(storage.sharedPreferences.all).isEqualTo(map)
+    }
+
+    private fun SharedPreferences.Editor.applySync() {
+        apply()
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+    }
+
+    companion object {
+        private const val NAME = "pref"
+        private const val MODE = Context.MODE_PRIVATE
+    }
+}
diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/TestUtils.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/TestUtils.kt
new file mode 100644
index 0000000..823d222
--- /dev/null
+++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/TestUtils.kt
@@ -0,0 +1,83 @@
+/*
+ * 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.settingslib.datastore
+
+import android.app.backup.BackupDataInput
+import android.app.backup.BackupDataInputStream
+import android.os.Build
+import java.io.ByteArrayInputStream
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.mock
+
+internal const val MAX_DATA_SIZE = 1 shl 12
+
+internal fun allCodecs() =
+    arrayOf<BackupCodec>(
+        BackupNoOpCodec(),
+    ) + zipCodecs()
+
+internal fun zipCodecs() =
+    arrayOf<BackupCodec>(
+        BackupZipCodec.DEFAULT_COMPRESSION,
+        BackupZipCodec.BEST_COMPRESSION,
+        BackupZipCodec.BEST_SPEED,
+    )
+
+internal fun <T : Any> Class<T>.newInstance(arg: Any, type: Class<*> = arg.javaClass): T =
+    getDeclaredConstructor(type).apply { isAccessible = true }.newInstance(arg)
+
+internal fun newBackupDataInputStream(
+    key: String,
+    data: ByteArray,
+    e: Exception? = null,
+): BackupDataInputStream {
+    // ShadowBackupDataOutput does not write data to file, so mock for reading data
+    val inputStream = ByteArrayInputStream(data)
+    val backupDataInput =
+        mock<BackupDataInput> {
+            on { readEntityData(any(), any(), any()) } doAnswer
+                {
+                    if (e != null) throw e
+                    val buf = it.arguments[0] as ByteArray
+                    val offset = it.arguments[1] as Int
+                    val size = it.arguments[2] as Int
+                    inputStream.read(buf, offset, size)
+                }
+        }
+    return BackupDataInputStream::class
+        .java
+        .newInstance(backupDataInput, BackupDataInput::class.java)
+        .apply {
+            setKey(key)
+            setDataSize(data.size)
+        }
+}
+
+internal fun BackupDataInputStream.setKey(value: Any) {
+    val field = javaClass.getDeclaredField("key")
+    field.isAccessible = true
+    field.set(this, value)
+}
+
+internal fun BackupDataInputStream.setDataSize(dataSize: Int) {
+    val field = javaClass.getDeclaredField("dataSize")
+    field.isAccessible = true
+    field.setInt(this, dataSize)
+}
+
+internal fun isRobolectric() = Build.FINGERPRINT.contains("robolectric")
diff --git a/packages/SettingsLib/aconfig/OWNERS b/packages/SettingsLib/aconfig/OWNERS
new file mode 100644
index 0000000..ba02d20
--- /dev/null
+++ b/packages/SettingsLib/aconfig/OWNERS
@@ -0,0 +1,2 @@
+# go/android-fwk-media-solutions for info on areas of ownership.
+per-file settingslib_media_flag_declarations.aconfig = file:platform/frameworks/av:/media/janitors/media_solutions_OWNERS
diff --git a/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig b/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig
index 4d70aec..7aae1a6 100644
--- a/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig
@@ -21,3 +21,13 @@
     description: "Enable Output Switcher when no media is playing."
     bug: "284227163"
 }
+
+flag {
+    name: "remove_unnecessary_route_scanning"
+    namespace: "media_solutions"
+    description: "Avoid active scan requests on UI components that only display route status information."
+    bug: "332515672"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index 3e29872..eae58ad 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -150,7 +150,16 @@
         mUserHandle = userHandle;
     }
 
-    /** Creates an instance of InfoMediaManager. */
+    /**
+     * Creates an instance of InfoMediaManager.
+     *
+     * @param context The {@link Context}.
+     * @param packageName The package name of the app for which to control routing, or null if the
+     *     caller is interested in system-level routing only (for example, headsets, built-in
+     *     speakers, as opposed to app-specific routing (for example, casting to another device).
+     * @param userHandle The {@link UserHandle} of the user on which the app to control is running,
+     *     or null if the caller does not need app-specific routing (see {@code packageName}).
+     */
     public static InfoMediaManager createInstance(
             Context context,
             @Nullable String packageName,
@@ -185,11 +194,7 @@
     }
 
     public void startScan() {
-        mMediaDevices.clear();
-        registerRouter();
         startScanOnRouter();
-        updateRouteListingPreference();
-        refreshDevices();
     }
 
     private void updateRouteListingPreference() {
@@ -203,7 +208,6 @@
 
     public final void stopScan() {
         stopScanOnRouter();
-        unregisterRouter();
     }
 
     protected abstract void stopScanOnRouter();
@@ -290,14 +294,37 @@
         return null;
     }
 
-    protected final void registerCallback(MediaDeviceCallback callback) {
+    /**
+     * Registers the specified {@code callback} to receive state updates about routing information.
+     *
+     * <p>As long as there is a registered {@link MediaDeviceCallback}, {@link InfoMediaManager}
+     * will receive state updates from the platform.
+     *
+     * <p>Call {@link #unregisterCallback(MediaDeviceCallback)} once you no longer need platform
+     * updates.
+     */
+    public final void registerCallback(@NonNull MediaDeviceCallback callback) {
+        boolean wasEmpty = mCallbacks.isEmpty();
         if (!mCallbacks.contains(callback)) {
             mCallbacks.add(callback);
+            if (wasEmpty) {
+                mMediaDevices.clear();
+                registerRouter();
+                updateRouteListingPreference();
+                refreshDevices();
+            }
         }
     }
 
-    protected final void unregisterCallback(MediaDeviceCallback callback) {
-        mCallbacks.remove(callback);
+    /**
+     * Unregisters the specified {@code callback}.
+     *
+     * @see #registerCallback(MediaDeviceCallback)
+     */
+    public final void unregisterCallback(@NonNull MediaDeviceCallback callback) {
+        if (mCallbacks.remove(callback) && mCallbacks.isEmpty()) {
+            unregisterRouter();
+        }
     }
 
     private void dispatchDeviceListAdded(@NonNull List<MediaDevice> devices) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index 0c2414c..a08a3dc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -17,7 +17,6 @@
 
 import static android.media.MediaRoute2ProviderService.REASON_UNKNOWN_ERROR;
 
-import android.app.Notification;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.content.ComponentName;
@@ -106,14 +105,23 @@
      * Register to start receiving callbacks for MediaDevice events.
      */
     public void registerCallback(DeviceCallback callback) {
-        mCallbacks.add(callback);
+        boolean wasEmpty = mCallbacks.isEmpty();
+        if (!mCallbacks.contains(callback)) {
+            mCallbacks.add(callback);
+            if (wasEmpty) {
+                mInfoMediaManager.registerCallback(mMediaDeviceCallback);
+            }
+        }
     }
 
     /**
      * Unregister to stop receiving callbacks for MediaDevice events
      */
     public void unregisterCallback(DeviceCallback callback) {
-        mCallbacks.remove(callback);
+        if (mCallbacks.remove(callback) && mCallbacks.isEmpty()) {
+            mInfoMediaManager.unregisterCallback(mMediaDeviceCallback);
+            unRegisterDeviceAttributeChangeCallback();
+        }
     }
 
     /**
@@ -125,7 +133,7 @@
      *
      * It will use {@link BluetoothAdapter#getDefaultAdapter()] for setting the bluetooth adapter.
      */
-    public LocalMediaManager(Context context, String packageName, Notification notification) {
+    public LocalMediaManager(Context context, String packageName) {
         mContext = context;
         mPackageName = packageName;
         mLocalBluetoothManager =
@@ -228,10 +236,6 @@
      * Start scan connected MediaDevice
      */
     public void startScan() {
-        synchronized (mMediaDevicesLock) {
-            mMediaDevices.clear();
-        }
-        mInfoMediaManager.registerCallback(mMediaDeviceCallback);
         mInfoMediaManager.startScan();
     }
 
@@ -281,9 +285,7 @@
      * Stop scan MediaDevice
      */
     public void stopScan() {
-        mInfoMediaManager.unregisterCallback(mMediaDeviceCallback);
         mInfoMediaManager.stopScan();
-        unRegisterDeviceAttributeChangeCallback();
     }
 
     /**
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt
index 724dd51..869fb7f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt
@@ -17,6 +17,7 @@
 
 import com.android.settingslib.media.LocalMediaManager
 import com.android.settingslib.media.MediaDevice
+import com.android.settingslib.media.flags.Flags
 import com.android.settingslib.volume.shared.AudioManagerEventsReceiver
 import com.android.settingslib.volume.shared.model.AudioManagerEvent
 import kotlinx.coroutines.CoroutineScope
@@ -69,10 +70,14 @@
                         }
                     }
                 localMediaManager.registerCallback(callback)
-                localMediaManager.startScan()
+                if (!Flags.removeUnnecessaryRouteScanning()) {
+                    localMediaManager.startScan()
+                }
 
                 awaitClose {
-                    localMediaManager.stopScan()
+                    if (!Flags.removeUnnecessaryRouteScanning()) {
+                        localMediaManager.stopScan()
+                    }
                     localMediaManager.unregisterCallback(callback)
                 }
             }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index a4b87da..69faddf 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -35,6 +35,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -751,35 +752,31 @@
 
     @Test
     public void onTransferred_getAvailableRoutes_shouldAddMediaDevice() {
-        final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
-        final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class);
-        routingSessionInfos.add(sessionInfo);
-        final List<String> selectedRoutes = new ArrayList<>();
-        selectedRoutes.add(TEST_ID);
-        when(sessionInfo.getSelectedRoutes()).thenReturn(selectedRoutes);
-        mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
+        mInfoMediaManager.mRouterManager = mRouterManager;
+        // Since test is running in Robolectric, return a fake session to avoid NPE.
+        when(mRouterManager.getRoutingSessions(anyString()))
+                .thenReturn(List.of(TEST_SYSTEM_ROUTING_SESSION));
+        when(mRouterManager.getSelectedRoutes(any()))
+                .thenReturn(List.of(TEST_SELECTED_SYSTEM_ROUTE));
 
-        final MediaRoute2Info info = mock(MediaRoute2Info.class);
         mInfoMediaManager.registerCallback(mCallback);
 
-        when(info.getDeduplicationIds()).thenReturn(Set.of());
-        when(info.getId()).thenReturn(TEST_ID);
-        when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+        MediaDevice mediaDevice = mInfoMediaManager.getCurrentConnectedDevice();
+        assertThat(mediaDevice).isNotNull();
+        assertThat(mediaDevice.getId()).isEqualTo(TEST_SYSTEM_ROUTE_ID);
 
-        final List<MediaRoute2Info> routes = new ArrayList<>();
-        routes.add(info);
-        mShadowRouter2Manager.setTransferableRoutes(routes);
+        when(mRouterManager.getRoutingSessions(anyString()))
+                .thenReturn(List.of(TEST_SYSTEM_ROUTING_SESSION, TEST_REMOTE_ROUTING_SESSION));
+        when(mRouterManager.getSelectedRoutes(any())).thenReturn(List.of(TEST_REMOTE_ROUTE));
 
-        final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
-        assertThat(mediaDevice).isNull();
-
-        mInfoMediaManager.mMediaRouterCallback.onTransferred(sessionInfo, sessionInfo);
+        mInfoMediaManager.mMediaRouterCallback.onTransferred(
+                TEST_SYSTEM_ROUTING_SESSION, TEST_REMOTE_ROUTING_SESSION);
 
         final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
-        assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
+        assertThat(infoDevice).isNotNull();
+        assertThat(infoDevice.getId()).isEqualTo(TEST_REMOTE_ROUTE.getId());
         assertThat(mInfoMediaManager.getCurrentConnectedDevice()).isEqualTo(infoDevice);
-        assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
-        verify(mCallback).onConnectedDeviceChanged(TEST_ID);
+        verify(mCallback).onConnectedDeviceChanged(TEST_REMOTE_ROUTE.getId());
     }
 
     @Test
@@ -795,7 +792,8 @@
 
         mInfoMediaManager.mMediaRouterCallback.onSessionUpdated(TEST_SYSTEM_ROUTING_SESSION);
 
-        verify(mCallback).onDeviceListAdded(any());
+        // Expecting 1st call after registerCallback() and 2nd call after onSessionUpdated().
+        verify(mCallback, times(2)).onDeviceListAdded(any());
     }
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
index 693b7d0..ddb5419 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
@@ -21,6 +21,8 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
@@ -58,6 +60,7 @@
 import org.robolectric.shadow.api.Shadow;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 @RunWith(RobolectricTestRunner.class)
@@ -117,6 +120,13 @@
         mInfoMediaManager = mock(
                 InfoMediaManager.class,
                 withSettings().useConstructor(mContext, TEST_PACKAGE_NAME, mLocalBluetoothManager));
+        doReturn(
+                        List.of(
+                                new RoutingSessionInfo.Builder(TEST_SESSION_ID, TEST_PACKAGE_NAME)
+                                        .addSelectedRoute(TEST_DEVICE_ID_1)
+                                        .build()))
+                .when(mInfoMediaManager)
+                .getRoutingSessionsForPackage();
 
         mInfoMediaDevice1 = spy(new InfoMediaDevice(mContext, mRouteInfo1));
         mInfoMediaDevice2 = new InfoMediaDevice(mContext, mRouteInfo2);
@@ -124,15 +134,16 @@
                 new LocalMediaManager(
                         mContext, mLocalBluetoothManager, mInfoMediaManager, TEST_PACKAGE_NAME);
         mLocalMediaManager.mAudioManager = mAudioManager;
+        mLocalMediaManager.registerCallback(mCallback);
+        clearInvocations(mCallback);
     }
 
     @Test
-    public void startScan_mediaDevicesListShouldBeClear() {
+    public void onDeviceListAdded_shouldClearDeviceList() {
         final MediaDevice device = mock(MediaDevice.class);
         mLocalMediaManager.mMediaDevices.add(device);
-
         assertThat(mLocalMediaManager.mMediaDevices).hasSize(1);
-        mLocalMediaManager.startScan();
+        mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(Collections.emptyList());
         assertThat(mLocalMediaManager.mMediaDevices).isEmpty();
     }
 
@@ -147,7 +158,6 @@
         when(device.getId()).thenReturn(TEST_DEVICE_ID_1);
         when(currentDevice.getId()).thenReturn(TEST_CURRENT_DEVICE_ID);
 
-        mLocalMediaManager.registerCallback(mCallback);
         assertThat(mLocalMediaManager.connectDevice(device)).isTrue();
         verify(mInfoMediaManager).connectToDevice(device);
     }
@@ -158,7 +168,6 @@
         mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice2);
         mLocalMediaManager.mCurrentConnectedDevice = mInfoMediaDevice1;
 
-        mLocalMediaManager.registerCallback(mCallback);
         assertThat(mLocalMediaManager.connectDevice(mInfoMediaDevice2)).isTrue();
 
         assertThat(mInfoMediaDevice2.getState()).isEqualTo(LocalMediaManager.MediaDeviceState
@@ -171,7 +180,6 @@
         mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice2);
         mLocalMediaManager.mCurrentConnectedDevice = mInfoMediaDevice1;
 
-        mLocalMediaManager.registerCallback(mCallback);
         assertThat(mLocalMediaManager.connectDevice(mInfoMediaDevice1)).isFalse();
 
         assertThat(mInfoMediaDevice1.getState()).isNotEqualTo(LocalMediaManager.MediaDeviceState
@@ -189,7 +197,6 @@
         when(cachedDevice.isConnected()).thenReturn(false);
         when(cachedDevice.isBusy()).thenReturn(false);
 
-        mLocalMediaManager.registerCallback(mCallback);
         assertThat(mLocalMediaManager.connectDevice(device)).isTrue();
 
         verify(cachedDevice).connect();
@@ -252,7 +259,6 @@
         when(device2.getId()).thenReturn(TEST_DEVICE_ID_2);
 
         assertThat(mLocalMediaManager.mMediaDevices).isEmpty();
-        mLocalMediaManager.registerCallback(mCallback);
         mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices);
 
         assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
@@ -274,7 +280,6 @@
         when(device3.getId()).thenReturn(TEST_DEVICE_ID_3);
 
         assertThat(mLocalMediaManager.mMediaDevices).hasSize(1);
-        mLocalMediaManager.registerCallback(mCallback);
         mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices);
 
         assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
@@ -292,7 +297,6 @@
         mLocalMediaManager.mMediaDevices.add(device2);
 
         assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
-        mLocalMediaManager.registerCallback(mCallback);
         mLocalMediaManager.mMediaDeviceCallback
                 .onDeviceListRemoved(mLocalMediaManager.mMediaDevices);
 
@@ -302,19 +306,21 @@
 
     @Test
     public void onDeviceListRemoved_phoneDeviceNotLastDeviceAfterRemoveDeviceList_removeList() {
-        final List<MediaDevice> devices = new ArrayList<>();
         final MediaDevice device1 = mock(MediaDevice.class);
         final MediaDevice device2 = mock(MediaDevice.class);
         final MediaDevice device3 = mock(MediaDevice.class);
-        devices.add(device1);
-        devices.add(device3);
+
+        mLocalMediaManager.mMediaDevices.clear();
         mLocalMediaManager.mMediaDevices.add(device1);
         mLocalMediaManager.mMediaDevices.add(device2);
         mLocalMediaManager.mMediaDevices.add(device3);
 
         assertThat(mLocalMediaManager.mMediaDevices).hasSize(3);
-        mLocalMediaManager.registerCallback(mCallback);
-        mLocalMediaManager.mMediaDeviceCallback.onDeviceListRemoved(devices);
+
+        final List<MediaDevice> devicesToRemove = new ArrayList<>();
+        devicesToRemove.add(device1);
+        devicesToRemove.add(device3);
+        mLocalMediaManager.mMediaDeviceCallback.onDeviceListRemoved(devicesToRemove);
 
         assertThat(mLocalMediaManager.mMediaDevices).hasSize(1);
         verify(mCallback).onDeviceListUpdate(any());
@@ -332,7 +338,6 @@
         when(device1.getId()).thenReturn(TEST_DEVICE_ID_1);
         when(device2.getId()).thenReturn(TEST_DEVICE_ID_2);
 
-        mLocalMediaManager.registerCallback(mCallback);
         mLocalMediaManager.mMediaDeviceCallback.onConnectedDeviceChanged(TEST_DEVICE_ID_2);
 
         assertThat(mLocalMediaManager.getCurrentConnectedDevice()).isEqualTo(device2);
@@ -352,7 +357,6 @@
         when(device1.getId()).thenReturn(TEST_DEVICE_ID_1);
         when(device2.getId()).thenReturn(TEST_DEVICE_ID_2);
 
-        mLocalMediaManager.registerCallback(mCallback);
         mLocalMediaManager.mMediaDeviceCallback.onConnectedDeviceChanged(TEST_DEVICE_ID_1);
 
         verify(mCallback, never()).onDeviceAttributesChanged();
@@ -366,7 +370,6 @@
 
         assertThat(mInfoMediaDevice1.getState()).isEqualTo(LocalMediaManager.MediaDeviceState
                 .STATE_DISCONNECTED);
-        mLocalMediaManager.registerCallback(mCallback);
         mLocalMediaManager.mMediaDeviceCallback.onConnectedDeviceChanged(TEST_DEVICE_ID_1);
 
         assertThat(mInfoMediaDevice1.getState()).isEqualTo(LocalMediaManager.MediaDeviceState
@@ -375,7 +378,6 @@
 
     @Test
     public void onConnectedDeviceChanged_nullConnectedDevice_noException() {
-        mLocalMediaManager.registerCallback(mCallback);
         mLocalMediaManager.mMediaDeviceCallback.onConnectedDeviceChanged(TEST_DEVICE_ID_2);
     }
 
@@ -392,7 +394,6 @@
         when(cachedDevice.isConnected()).thenReturn(false);
         when(cachedDevice.isBusy()).thenReturn(false);
 
-        mLocalMediaManager.registerCallback(mCallback);
         mLocalMediaManager.connectDevice(device);
 
         mLocalMediaManager.mDeviceAttributeChangeCallback.onDeviceAttributesChanged();
@@ -410,7 +411,6 @@
                 .STATE_CONNECTING);
         assertThat(mInfoMediaDevice2.getState()).isEqualTo(LocalMediaManager.MediaDeviceState
                 .STATE_CONNECTED);
-        mLocalMediaManager.registerCallback(mCallback);
         mLocalMediaManager.mMediaDeviceCallback.onRequestFailed(REASON_UNKNOWN_ERROR);
 
         assertThat(mInfoMediaDevice1.getState()).isEqualTo(LocalMediaManager.MediaDeviceState
@@ -421,8 +421,6 @@
 
     @Test
     public void onDeviceAttributesChanged_shouldBeCalled() {
-        mLocalMediaManager.registerCallback(mCallback);
-
         mLocalMediaManager.mDeviceAttributeChangeCallback.onDeviceAttributesChanged();
 
         verify(mCallback).onDeviceAttributesChanged();
@@ -475,7 +473,6 @@
         when(device1.getDeviceType()).thenReturn(MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE);
 
         assertThat(mLocalMediaManager.mMediaDevices).hasSize(0);
-        mLocalMediaManager.registerCallback(mCallback);
         mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices);
 
         assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
@@ -497,7 +494,6 @@
         when(cachedDevice.isConnected()).thenReturn(false);
         when(cachedDevice.isBusy()).thenReturn(false);
 
-        mLocalMediaManager.registerCallback(mCallback);
         mLocalMediaManager.connectDevice(device);
 
         verify(cachedDevice).connect();
@@ -512,8 +508,6 @@
 
     @Test
     public void onRequestFailed_shouldDispatchOnRequestFailed() {
-        mLocalMediaManager.registerCallback(mCallback);
-
         mLocalMediaManager.mMediaDeviceCallback.onRequestFailed(1);
 
         verify(mCallback).onRequestFailed(1);
@@ -532,7 +526,6 @@
         mShadowBluetoothAdapter = null;
 
         assertThat(mLocalMediaManager.mMediaDevices).hasSize(1);
-        mLocalMediaManager.registerCallback(mCallback);
         mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices);
 
         assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
index ad3eb92..e77cf2f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
@@ -531,13 +531,22 @@
             pw.println("  put NAMESPACE KEY VALUE [default]");
             pw.println("      Change the contents of KEY to VALUE for the given NAMESPACE.");
             pw.println("      {default} to set as the default value.");
+            pw.println("  override NAMESPACE KEY VALUE");
+            pw.println("      Set flag NAMESPACE/KEY to the given VALUE, and ignores "
+                    + "server-updates for");
+            pw.println("      this flag. This can still be called even if there is no underlying "
+                    + "value set.");
             pw.println("  delete NAMESPACE KEY");
             pw.println("      Delete the entry for KEY for the given NAMESPACE.");
+            pw.println("  clear_override NAMESPACE KEY");
+            pw.println("      Clear local sticky flag override for KEY in the given NAMESPACE.");
             pw.println("  list_namespaces [--public]");
             pw.println("      Prints the name of all (or just the public) namespaces.");
             pw.println("  list [NAMESPACE]");
             pw.println("      Print all keys and values defined, optionally for the given "
                     + "NAMESPACE.");
+            pw.println("  list_local_overrides");
+            pw.println("      Print all flags that have been overridden.");
             pw.println("  reset RESET_MODE [NAMESPACE]");
             pw.println("      Reset all flag values, optionally for a NAMESPACE, according to "
                     + "RESET_MODE.");
@@ -547,8 +556,9 @@
                     + "flags are reset");
             pw.println("  set_sync_disabled_for_tests SYNC_DISABLED_MODE");
             pw.println("      Modifies bulk property setting behavior for tests. When in one of the"
-                    + " disabled modes this ensures that config isn't overwritten.");
-            pw.println("      SYNC_DISABLED_MODE is one of:");
+                    + " disabled modes");
+            pw.println("      this ensures that config isn't overwritten. SYNC_DISABLED_MODE is "
+                    + "one of:");
             pw.println("        none: Sync is not disabled. A reboot may be required to restart"
                     + " syncing.");
             pw.println("        persistent: Sync is disabled, this state will survive a reboot.");
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 40db52e..c88c373 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -117,6 +117,7 @@
         "SystemUILogLib",
         "SystemUIPluginLib",
         "SystemUISharedLib",
+        "SystemUI-shared-utils",
         "SystemUI-statsd",
         "SettingsLib",
         "com_android_systemui_flags_lib",
@@ -263,6 +264,7 @@
         "SystemUISharedLib",
         "SystemUICustomizationLib",
         "SystemUICustomizationTestUtils",
+        "SystemUI-shared-utils",
         "SystemUI-statsd",
         "SettingsLib",
         "com_android_systemui_flags_lib",
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
index 9bd6f81..84b1a4b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
@@ -62,6 +62,7 @@
 import com.android.compose.animation.scene.UserAction
 import com.android.compose.animation.scene.UserActionResult
 import com.android.compose.animation.scene.animateSceneFloatAsState
+import com.android.compose.modifiers.padding
 import com.android.compose.modifiers.thenIf
 import com.android.systemui.battery.BatteryMeterViewController
 import com.android.systemui.dagger.SysUISingleton
@@ -289,6 +290,18 @@
         remember(lifecycleOwner, viewModel) { viewModel.getFooterActionsViewModel(lifecycleOwner) }
     val tileSquishiness by
         animateSceneFloatAsState(value = 1f, key = QuickSettings.SharedValues.TilesSquishiness)
+    val unfoldTranslationXForStartSide by
+        viewModel
+            .unfoldTranslationX(
+                isOnStartSide = true,
+            )
+            .collectAsState(0f)
+    val unfoldTranslationXForEndSide by
+        viewModel
+            .unfoldTranslationX(
+                isOnStartSide = false,
+            )
+            .collectAsState(0f)
 
     val navBarBottomHeight = WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding()
     val density = LocalDensity.current
@@ -337,10 +350,18 @@
                 modifier =
                     Modifier.padding(horizontal = Shade.Dimensions.HorizontalPadding)
                         .then(brightnessMirrorShowingModifier)
+                        .padding(
+                            horizontal = { unfoldTranslationXForStartSide.roundToInt() },
+                        )
             )
 
             Row(modifier = Modifier.fillMaxWidth().weight(1f)) {
-                Box(modifier = Modifier.weight(1f)) {
+                Box(
+                    modifier =
+                        Modifier.weight(1f).graphicsLayer {
+                            translationX = unfoldTranslationXForStartSide
+                        },
+                ) {
                     BrightnessMirror(
                         viewModel = viewModel.brightnessMirrorViewModel,
                         qsSceneAdapter = viewModel.qsSceneAdapter,
@@ -407,7 +428,7 @@
                         Modifier.weight(1f)
                             .fillMaxHeight()
                             .padding(bottom = navBarBottomHeight)
-                            .then(brightnessMirrorShowingModifier),
+                            .then(brightnessMirrorShowingModifier)
                 )
             }
         }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt
index e15d315..0893b9d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt
@@ -69,9 +69,19 @@
                             role = Role.Button
                             contentDescription = label
                         },
-                    color = MaterialTheme.colorScheme.tertiaryContainer,
+                    color =
+                        if (viewModel.isActive) {
+                            MaterialTheme.colorScheme.tertiaryContainer
+                        } else {
+                            MaterialTheme.colorScheme.surface
+                        },
                     shape = RoundedCornerShape(28.dp),
-                    contentColor = MaterialTheme.colorScheme.onTertiaryContainer,
+                    contentColor =
+                        if (viewModel.isActive) {
+                            MaterialTheme.colorScheme.onTertiaryContainer
+                        } else {
+                            MaterialTheme.colorScheme.onSurface
+                        },
                     onClick = onClick,
                 ) {
                     Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt
index b2351c4..4f3a6c8 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt
@@ -40,14 +40,14 @@
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.dp
 import com.android.systemui.common.ui.compose.Icon
-import com.android.systemui.volume.panel.component.button.ui.viewmodel.ToggleButtonViewModel
+import com.android.systemui.volume.panel.component.button.ui.viewmodel.ButtonViewModel
 import com.android.systemui.volume.panel.ui.composable.ComposeVolumePanelUiComponent
 import com.android.systemui.volume.panel.ui.composable.VolumePanelComposeScope
 import kotlinx.coroutines.flow.StateFlow
 
 /** [ComposeVolumePanelUiComponent] implementing a toggleable button from a bottom row. */
 class ToggleButtonComponent(
-    private val viewModelFlow: StateFlow<ToggleButtonViewModel?>,
+    private val viewModelFlow: StateFlow<ButtonViewModel?>,
     private val onCheckedChange: (isChecked: Boolean) -> Unit
 ) : ComposeVolumePanelUiComponent {
 
@@ -64,7 +64,7 @@
         ) {
             BottomComponentButtonSurface {
                 val colors =
-                    if (viewModel.isChecked) {
+                    if (viewModel.isActive) {
                         ButtonDefaults.buttonColors(
                             containerColor = MaterialTheme.colorScheme.tertiaryContainer,
                             contentColor = MaterialTheme.colorScheme.onTertiaryContainer,
@@ -81,7 +81,7 @@
                             role = Role.Switch
                             contentDescription = label
                         },
-                    onClick = { onCheckedChange(!viewModel.isChecked) },
+                    onClick = { onCheckedChange(!viewModel.isActive) },
                     shape = RoundedCornerShape(28.dp),
                     colors = colors
                 ) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
index f377fa6..12d2bc2 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
@@ -52,7 +52,7 @@
             VolumePanelUiEvent.VOLUME_PANEL_SPATIAL_AUDIO_POP_UP_SHOWN,
             0,
             null,
-            viewModel.spatialAudioButtons.value.indexOfFirst { it.button.isChecked }
+            viewModel.spatialAudioButtons.value.indexOfFirst { it.button.isActive }
         )
         volumePanelPopup.show(expandable, { Title() }, { Content(it) })
     }
@@ -85,7 +85,7 @@
             for (buttonViewModel in enabledModelStates) {
                 val label = buttonViewModel.button.label.toString()
                 item(
-                    isSelected = buttonViewModel.button.isChecked,
+                    isSelected = buttonViewModel.button.isActive,
                     onItemSelected = { viewModel.setEnabled(buttonViewModel.model) },
                     contentDescription = label,
                     icon = { Icon(icon = buttonViewModel.button.icon) },
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
index 910cd5e..1bf541a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.volume.panel.ui.composable
 
-import androidx.compose.foundation.clickable
+import androidx.compose.foundation.gestures.detectTapGestures
 import androidx.compose.foundation.isSystemInDarkTheme
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
@@ -38,6 +38,7 @@
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.pointer.pointerInput
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.res.dimensionResource
@@ -75,21 +76,13 @@
                 modifier =
                     modifier
                         .fillMaxSize()
-                        .clickable(onClick = onDismiss)
+                        .volumePanelClick(onDismiss)
                         .volumePanelPaddings(isPortrait = isPortrait),
                 contentAlignment = Alignment.BottomCenter,
             ) {
                 val radius = dimensionResource(R.dimen.volume_panel_corner_radius)
                 Surface(
-                    modifier =
-                        Modifier.clickable(
-                            interactionSource = null,
-                            indication = null,
-                            onClick = {
-                                // prevent windowCloseOnTouchOutside from dismissing when tapped
-                                // on the panel itself.
-                            },
-                        ),
+                    modifier = Modifier.volumePanelClick {},
                     shape = RoundedCornerShape(topStart = radius, topEnd = radius),
                     color = MaterialTheme.colorScheme.surfaceContainer,
                 ) {
@@ -185,3 +178,13 @@
         )
     }
 }
+
+/**
+ * For some reason adding clickable modifier onto the VolumePanel affects the traversal order:
+ * b/331155283.
+ *
+ * TODO(b/334870995) revert this to Modifier.clickable
+ */
+@Composable
+private fun Modifier.volumePanelClick(onClick: () -> Unit) =
+    pointerInput(onClick) { detectTapGestures { onClick() } }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index 85774c6..60b48f2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -571,7 +571,7 @@
                 // THEN the view layout is never updated
                 verify(windowManager, never()).updateViewLayout(any(), any())
 
-                // CLEANUPL we hide to end the job that listens for the finishedGoingToSleep signal
+                // CLEANUP we hide to end the job that listens for the finishedGoingToSleep signal
                 controllerOverlay.hide()
             }
         }
@@ -595,7 +595,7 @@
                 controllerOverlay.updateOverlayParams(overlayParams)
 
                 // THEN the view layout is updated
-                verify(windowManager, never()).updateViewLayout(any(), any())
+                verify(windowManager).updateViewLayout(any(), any())
             }
         }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
deleted file mode 100644
index 4e18a47..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
+++ /dev/null
@@ -1,559 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.dreams;
-
-import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.ComponentName;
-import android.content.Intent;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.service.dreams.IDreamOverlay;
-import android.service.dreams.IDreamOverlayCallback;
-import android.service.dreams.IDreamOverlayClient;
-import android.service.dreams.IDreamOverlayClientCallback;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.view.WindowManagerImpl;
-
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleRegistry;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.logging.UiEventLogger;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.ambient.touch.TouchMonitor;
-import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent;
-import com.android.systemui.complication.ComplicationLayoutEngine;
-import com.android.systemui.dreams.complication.HideComplicationTouchHandler;
-import com.android.systemui.dreams.complication.dagger.ComplicationComponent;
-import com.android.systemui.dreams.dagger.DreamOverlayComponent;
-import com.android.systemui.touch.TouchInsetManager;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.time.FakeSystemClock;
-import com.android.systemui.utils.leaks.LeakCheckedTest;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class DreamOverlayServiceTest extends SysuiTestCase {
-    private static final ComponentName LOW_LIGHT_COMPONENT = new ComponentName("package",
-            "lowlight");
-
-    private static final ComponentName HOME_CONTROL_PANEL_DREAM_COMPONENT =
-            new ComponentName("package", "homeControlPanel");
-    private static final String DREAM_COMPONENT = "package/dream";
-    private static final String WINDOW_NAME = "test";
-    private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
-    private final FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock);
-
-    @Mock
-    DreamOverlayLifecycleOwner mLifecycleOwner;
-
-    @Mock
-    LifecycleRegistry mLifecycleRegistry;
-
-    @Rule
-    public final LeakCheckedTest.SysuiLeakCheck mLeakCheck = new LeakCheckedTest.SysuiLeakCheck();
-
-    WindowManager.LayoutParams mWindowParams;
-
-    @Mock
-    IDreamOverlayCallback mDreamOverlayCallback;
-
-    @Mock
-    WindowManagerImpl mWindowManager;
-
-    @Mock
-    com.android.systemui.complication.dagger.ComplicationComponent.Factory
-            mComplicationComponentFactory;
-
-    @Mock
-    com.android.systemui.complication.dagger.ComplicationComponent mComplicationComponent;
-
-    @Mock
-    ComplicationLayoutEngine mComplicationVisibilityController;
-
-    @Mock
-    ComplicationComponent.Factory mDreamComplicationComponentFactory;
-
-    @Mock
-    ComplicationComponent mDreamComplicationComponent;
-
-    @Mock
-    HideComplicationTouchHandler mHideComplicationTouchHandler;
-
-    @Mock
-    DreamOverlayComponent.Factory mDreamOverlayComponentFactory;
-
-    @Mock
-    DreamOverlayComponent mDreamOverlayComponent;
-
-    @Mock
-    AmbientTouchComponent.Factory mAmbientTouchComponentFactory;
-
-    @Mock
-    AmbientTouchComponent mAmbientTouchComponent;
-
-    @Mock
-    DreamOverlayContainerView mDreamOverlayContainerView;
-
-    @Mock
-    DreamOverlayContainerViewController mDreamOverlayContainerViewController;
-
-    @Mock
-    KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-
-    @Mock
-    TouchMonitor mTouchMonitor;
-
-    @Mock
-    DreamOverlayStateController mStateController;
-
-    @Mock
-    ViewGroup mDreamOverlayContainerViewParent;
-
-    @Mock
-    TouchInsetManager mTouchInsetManager;
-
-    @Mock
-    UiEventLogger mUiEventLogger;
-
-    @Mock
-    DreamOverlayCallbackController mDreamOverlayCallbackController;
-
-    @Captor
-    ArgumentCaptor<View> mViewCaptor;
-
-    DreamOverlayService mService;
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-
-        when(mDreamOverlayComponent.getDreamOverlayContainerViewController())
-                .thenReturn(mDreamOverlayContainerViewController);
-        when(mLifecycleOwner.getRegistry())
-                .thenReturn(mLifecycleRegistry);
-        when(mComplicationComponentFactory
-                .create(any(), any(), any(), any()))
-                .thenReturn(mComplicationComponent);
-        when(mComplicationComponent.getVisibilityController())
-                .thenReturn(mComplicationVisibilityController);
-        when(mDreamComplicationComponent.getHideComplicationTouchHandler())
-                .thenReturn(mHideComplicationTouchHandler);
-        when(mDreamComplicationComponentFactory
-                .create(any(), any()))
-                .thenReturn(mDreamComplicationComponent);
-        when(mDreamOverlayComponentFactory
-                .create(any(), any(), any()))
-                .thenReturn(mDreamOverlayComponent);
-        when(mAmbientTouchComponentFactory.create(any(), any())).thenReturn(mAmbientTouchComponent);
-        when(mAmbientTouchComponent.getTouchMonitor())
-                .thenReturn(mTouchMonitor);
-        when(mDreamOverlayContainerViewController.getContainerView())
-                .thenReturn(mDreamOverlayContainerView);
-
-        mWindowParams = new WindowManager.LayoutParams();
-        mService = new DreamOverlayService(
-                mContext,
-                mLifecycleOwner,
-                mMainExecutor,
-                mWindowManager,
-                mComplicationComponentFactory,
-                mDreamComplicationComponentFactory,
-                mDreamOverlayComponentFactory,
-                mAmbientTouchComponentFactory,
-                mStateController,
-                mKeyguardUpdateMonitor,
-                mUiEventLogger,
-                mTouchInsetManager,
-                LOW_LIGHT_COMPONENT,
-                HOME_CONTROL_PANEL_DREAM_COMPONENT,
-                mDreamOverlayCallbackController,
-                WINDOW_NAME);
-    }
-
-    public IDreamOverlayClient getClient() throws RemoteException {
-        final IBinder proxy = mService.onBind(new Intent());
-        final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
-        final IDreamOverlayClientCallback callback =
-                Mockito.mock(IDreamOverlayClientCallback.class);
-        overlay.getClient(callback);
-        final ArgumentCaptor<IDreamOverlayClient> clientCaptor =
-                ArgumentCaptor.forClass(IDreamOverlayClient.class);
-        verify(callback).onDreamOverlayClient(clientCaptor.capture());
-
-        return clientCaptor.getValue();
-    }
-
-    @Test
-    public void testOnStartMetricsLogged() throws Exception {
-        final IDreamOverlayClient client = getClient();
-
-        // Inform the overlay service of dream starting.
-        client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
-                false /*shouldShowComplication*/);
-        mMainExecutor.runAllReady();
-
-        verify(mUiEventLogger).log(DreamOverlayService.DreamOverlayEvent.DREAM_OVERLAY_ENTER_START);
-        verify(mUiEventLogger).log(
-                DreamOverlayService.DreamOverlayEvent.DREAM_OVERLAY_COMPLETE_START);
-    }
-
-    @Test
-    public void testOverlayContainerViewAddedToWindow() throws Exception {
-        final IDreamOverlayClient client = getClient();
-
-        // Inform the overlay service of dream starting.
-        client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
-                false /*shouldShowComplication*/);
-        mMainExecutor.runAllReady();
-
-        verify(mWindowManager).addView(any(), any());
-    }
-
-    // Validates that {@link DreamOverlayService} properly handles the case where the dream's
-    // window is no longer valid by the time start is called.
-    @Test
-    public void testInvalidWindowAddStart() throws Exception {
-        final IDreamOverlayClient client = getClient();
-
-        doThrow(new WindowManager.BadTokenException()).when(mWindowManager).addView(any(), any());
-        // Inform the overlay service of dream starting.
-        client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
-                false /*shouldShowComplication*/);
-        mMainExecutor.runAllReady();
-
-        verify(mWindowManager).addView(any(), any());
-
-        verify(mStateController).setOverlayActive(false);
-        verify(mStateController).setLowLightActive(false);
-        verify(mStateController).setEntryAnimationsFinished(false);
-
-        verify(mStateController, never()).setOverlayActive(true);
-        verify(mUiEventLogger, never()).log(
-                DreamOverlayService.DreamOverlayEvent.DREAM_OVERLAY_COMPLETE_START);
-
-        verify(mDreamOverlayCallbackController, never()).onStartDream();
-    }
-
-    @Test
-    public void testDreamOverlayContainerViewControllerInitialized() throws Exception {
-        final IDreamOverlayClient client = getClient();
-
-        // Inform the overlay service of dream starting.
-        client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
-                false /*shouldShowComplication*/);
-        mMainExecutor.runAllReady();
-
-        verify(mDreamOverlayContainerViewController).init();
-    }
-
-    @Test
-    public void testDreamOverlayContainerViewRemovedFromOldParentWhenInitialized()
-            throws Exception {
-        when(mDreamOverlayContainerView.getParent())
-                .thenReturn(mDreamOverlayContainerViewParent)
-                .thenReturn(null);
-
-        final IDreamOverlayClient client = getClient();
-
-        // Inform the overlay service of dream starting.
-        client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
-                false /*shouldShowComplication*/);
-        mMainExecutor.runAllReady();
-
-        verify(mDreamOverlayContainerViewParent).removeView(mDreamOverlayContainerView);
-    }
-
-    @Test
-    public void testShouldShowComplicationsSetByStartDream() throws RemoteException {
-        final IDreamOverlayClient client = getClient();
-
-        // Inform the overlay service of dream starting.
-        client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
-                true /*shouldShowComplication*/);
-        mMainExecutor.runAllReady();
-
-        assertThat(mService.shouldShowComplications()).isTrue();
-    }
-
-    @Test
-    public void testLowLightSetByStartDream() throws RemoteException {
-        final IDreamOverlayClient client = getClient();
-
-        // Inform the overlay service of dream starting.
-        client.startDream(mWindowParams, mDreamOverlayCallback,
-                LOW_LIGHT_COMPONENT.flattenToString(), false /*shouldShowComplication*/);
-        mMainExecutor.runAllReady();
-
-        assertThat(mService.getDreamComponent()).isEqualTo(LOW_LIGHT_COMPONENT);
-        verify(mStateController).setLowLightActive(true);
-    }
-
-    @Test
-    public void testHomeControlPanelSetsByStartDream() throws RemoteException {
-        final IDreamOverlayClient client = getClient();
-
-        // Inform the overlay service of dream starting.
-        client.startDream(mWindowParams, mDreamOverlayCallback,
-                HOME_CONTROL_PANEL_DREAM_COMPONENT.flattenToString(),
-                false /*shouldShowComplication*/);
-        mMainExecutor.runAllReady();
-        assertThat(mService.getDreamComponent()).isEqualTo(HOME_CONTROL_PANEL_DREAM_COMPONENT);
-        verify(mStateController).setHomeControlPanelActive(true);
-    }
-
-    @Test
-    public void testOnEndDream() throws RemoteException {
-        final IDreamOverlayClient client = getClient();
-
-        // Inform the overlay service of dream starting.
-        client.startDream(mWindowParams, mDreamOverlayCallback,
-                LOW_LIGHT_COMPONENT.flattenToString(), false /*shouldShowComplication*/);
-        mMainExecutor.runAllReady();
-
-        // Verify view added.
-        verify(mWindowManager).addView(mViewCaptor.capture(), any());
-
-        // Service destroyed.
-        mService.onEndDream();
-        mMainExecutor.runAllReady();
-
-        // Verify view removed.
-        verify(mWindowManager).removeView(mViewCaptor.getValue());
-
-        // Verify state correctly set.
-        verify(mStateController).setOverlayActive(false);
-        verify(mStateController).setLowLightActive(false);
-        verify(mStateController).setEntryAnimationsFinished(false);
-    }
-
-    @Test
-    public void testImmediateEndDream() throws Exception {
-        final IDreamOverlayClient client = getClient();
-
-        // Start the dream, but don't execute any Runnables put on the executor yet. We delay
-        // executing Runnables as the timing isn't guaranteed and we want to verify that the overlay
-        // starts and finishes in the proper order even if Runnables are delayed.
-        client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
-                false /*shouldShowComplication*/);
-        // Immediately end the dream.
-        client.endDream();
-        // Run any scheduled Runnables.
-        mMainExecutor.runAllReady();
-
-        // The overlay starts then finishes.
-        InOrder inOrder = inOrder(mWindowManager);
-        inOrder.verify(mWindowManager).addView(mViewCaptor.capture(), any());
-        inOrder.verify(mWindowManager).removeView(mViewCaptor.getValue());
-    }
-
-    @Test
-    public void testEndDreamDuringStartDream() throws Exception {
-        final IDreamOverlayClient client = getClient();
-
-        // Schedule the endDream call in the middle of the startDream implementation, as any
-        // ordering is possible.
-        doAnswer(invocation -> {
-            client.endDream();
-            return null;
-        }).when(mStateController).setOverlayActive(true);
-
-        // Start the dream.
-        client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
-                false /*shouldShowComplication*/);
-        mMainExecutor.runAllReady();
-
-        // The overlay starts then finishes.
-        InOrder inOrder = inOrder(mWindowManager);
-        inOrder.verify(mWindowManager).addView(mViewCaptor.capture(), any());
-        inOrder.verify(mWindowManager).removeView(mViewCaptor.getValue());
-    }
-
-    @Test
-    public void testDestroy() throws RemoteException {
-        final IDreamOverlayClient client = getClient();
-
-        // Inform the overlay service of dream starting.
-        client.startDream(mWindowParams, mDreamOverlayCallback,
-                LOW_LIGHT_COMPONENT.flattenToString(), false /*shouldShowComplication*/);
-        mMainExecutor.runAllReady();
-
-        // Verify view added.
-        verify(mWindowManager).addView(mViewCaptor.capture(), any());
-
-        // Service destroyed.
-        mService.onDestroy();
-        mMainExecutor.runAllReady();
-
-        // Verify view removed.
-        verify(mWindowManager).removeView(mViewCaptor.getValue());
-
-        // Verify state correctly set.
-        verify(mKeyguardUpdateMonitor).removeCallback(any());
-        verify(mLifecycleRegistry).setCurrentState(Lifecycle.State.DESTROYED);
-        verify(mStateController).setOverlayActive(false);
-        verify(mStateController).setLowLightActive(false);
-        verify(mStateController).setEntryAnimationsFinished(false);
-    }
-
-    @Test
-    public void testDoNotRemoveViewOnDestroyIfOverlayNotStarted() {
-        // Service destroyed without ever starting dream.
-        mService.onDestroy();
-        mMainExecutor.runAllReady();
-
-        // Verify no view is removed.
-        verify(mWindowManager, never()).removeView(any());
-
-        // Verify state still correctly set.
-        verify(mKeyguardUpdateMonitor).removeCallback(any());
-        verify(mLifecycleRegistry).setCurrentState(Lifecycle.State.DESTROYED);
-        verify(mStateController).setOverlayActive(false);
-        verify(mStateController).setLowLightActive(false);
-    }
-
-    @Test
-    public void testDecorViewNotAddedToWindowAfterDestroy() throws Exception {
-        final IDreamOverlayClient client = getClient();
-
-        // Destroy the service.
-        mService.onDestroy();
-        mMainExecutor.runAllReady();
-
-        // Inform the overlay service of dream starting.
-        client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
-                false /*shouldShowComplication*/);
-        mMainExecutor.runAllReady();
-
-        verify(mWindowManager, never()).addView(any(), any());
-    }
-
-    @Test
-    public void testNeverRemoveDecorViewIfNotAdded() {
-        // Service destroyed before dream started.
-        mService.onDestroy();
-        mMainExecutor.runAllReady();
-
-        verify(mWindowManager, never()).removeView(any());
-    }
-
-    @Test
-    public void testResetCurrentOverlayWhenConnectedToNewDream() throws RemoteException {
-        final IDreamOverlayClient client = getClient();
-
-        // Inform the overlay service of dream starting. Do not show dream complications.
-        client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
-                false /*shouldShowComplication*/);
-        mMainExecutor.runAllReady();
-
-        // Verify that a new window is added.
-        verify(mWindowManager).addView(mViewCaptor.capture(), any());
-        final View windowDecorView = mViewCaptor.getValue();
-
-        // Assert that the overlay is not showing complications.
-        assertThat(mService.shouldShowComplications()).isFalse();
-
-        clearInvocations(mDreamOverlayComponent);
-        clearInvocations(mAmbientTouchComponent);
-        clearInvocations(mWindowManager);
-
-        // New dream starting with dream complications showing. Note that when a new dream is
-        // binding to the dream overlay service, it receives the same instance of IBinder as the
-        // first one.
-        client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
-                true /*shouldShowComplication*/);
-        mMainExecutor.runAllReady();
-
-        // Assert that the overlay is showing complications.
-        assertThat(mService.shouldShowComplications()).isTrue();
-
-        // Verify that the old overlay window has been removed, and a new one created.
-        verify(mWindowManager).removeView(windowDecorView);
-        verify(mWindowManager).addView(any(), any());
-
-        // Verify that new instances of overlay container view controller and overlay touch monitor
-        // are created.
-        verify(mDreamOverlayComponent).getDreamOverlayContainerViewController();
-        verify(mAmbientTouchComponent).getTouchMonitor();
-    }
-
-    @Test
-    public void testWakeUp() throws RemoteException {
-        final IDreamOverlayClient client = getClient();
-
-        // Inform the overlay service of dream starting.
-        client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
-                true /*shouldShowComplication*/);
-        mMainExecutor.runAllReady();
-
-        mService.onWakeUp();
-        verify(mDreamOverlayContainerViewController).wakeUp();
-        verify(mDreamOverlayCallbackController).onWakeUp();
-    }
-
-    @Test
-    public void testWakeUpBeforeStartDoesNothing() {
-        mService.onWakeUp();
-        verify(mDreamOverlayContainerViewController, never()).wakeUp();
-    }
-
-    @Test
-    public void testSystemFlagShowForAllUsersSetOnWindow() throws RemoteException {
-        final IDreamOverlayClient client = getClient();
-
-        // Inform the overlay service of dream starting. Do not show dream complications.
-        client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT,
-                false /*shouldShowComplication*/);
-        mMainExecutor.runAllReady();
-
-        final ArgumentCaptor<WindowManager.LayoutParams> paramsCaptor =
-                ArgumentCaptor.forClass(WindowManager.LayoutParams.class);
-
-        // Verify that a new window is added.
-        verify(mWindowManager).addView(any(), paramsCaptor.capture());
-
-        assertThat((paramsCaptor.getValue().privateFlags & SYSTEM_FLAG_SHOW_FOR_ALL_USERS)
-                == SYSTEM_FLAG_SHOW_FOR_ALL_USERS).isTrue();
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
new file mode 100644
index 0000000..b18a8ec
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
@@ -0,0 +1,573 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.dreams
+
+import android.content.ComponentName
+import android.content.Intent
+import android.os.RemoteException
+import android.service.dreams.IDreamOverlay
+import android.service.dreams.IDreamOverlayCallback
+import android.service.dreams.IDreamOverlayClient
+import android.service.dreams.IDreamOverlayClientCallback
+import android.view.View
+import android.view.ViewGroup
+import android.view.WindowManager
+import android.view.WindowManagerImpl
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.UiEventLogger
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.ambient.touch.TouchMonitor
+import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent
+import com.android.systemui.complication.ComplicationHostViewController
+import com.android.systemui.complication.ComplicationLayoutEngine
+import com.android.systemui.complication.dagger.ComplicationComponent
+import com.android.systemui.dreams.complication.HideComplicationTouchHandler
+import com.android.systemui.dreams.dagger.DreamOverlayComponent
+import com.android.systemui.touch.TouchInsetManager
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.MockitoAnnotations
+import org.mockito.invocation.InvocationOnMock
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DreamOverlayServiceTest : SysuiTestCase() {
+    private val mFakeSystemClock = FakeSystemClock()
+    private val mMainExecutor = FakeExecutor(mFakeSystemClock)
+
+    @Mock lateinit var mLifecycleOwner: DreamOverlayLifecycleOwner
+
+    @Mock lateinit var mLifecycleRegistry: LifecycleRegistry
+
+    lateinit var mWindowParams: WindowManager.LayoutParams
+
+    @Mock lateinit var mDreamOverlayCallback: IDreamOverlayCallback
+
+    @Mock lateinit var mWindowManager: WindowManagerImpl
+
+    @Mock lateinit var mComplicationComponentFactory: ComplicationComponent.Factory
+
+    @Mock lateinit var mComplicationComponent: ComplicationComponent
+
+    @Mock lateinit var mComplicationHostViewController: ComplicationHostViewController
+
+    @Mock lateinit var mComplicationVisibilityController: ComplicationLayoutEngine
+
+    @Mock
+    lateinit var mDreamComplicationComponentFactory:
+        com.android.systemui.dreams.complication.dagger.ComplicationComponent.Factory
+
+    @Mock
+    lateinit var mDreamComplicationComponent:
+        com.android.systemui.dreams.complication.dagger.ComplicationComponent
+
+    @Mock lateinit var mHideComplicationTouchHandler: HideComplicationTouchHandler
+
+    @Mock lateinit var mDreamOverlayComponentFactory: DreamOverlayComponent.Factory
+
+    @Mock lateinit var mDreamOverlayComponent: DreamOverlayComponent
+
+    @Mock lateinit var mAmbientTouchComponentFactory: AmbientTouchComponent.Factory
+
+    @Mock lateinit var mAmbientTouchComponent: AmbientTouchComponent
+
+    @Mock lateinit var mDreamOverlayContainerView: DreamOverlayContainerView
+
+    @Mock lateinit var mDreamOverlayContainerViewController: DreamOverlayContainerViewController
+
+    @Mock lateinit var mKeyguardUpdateMonitor: KeyguardUpdateMonitor
+
+    @Mock lateinit var mTouchMonitor: TouchMonitor
+
+    @Mock lateinit var mStateController: DreamOverlayStateController
+
+    @Mock lateinit var mDreamOverlayContainerViewParent: ViewGroup
+
+    @Mock lateinit var mTouchInsetManager: TouchInsetManager
+
+    @Mock lateinit var mUiEventLogger: UiEventLogger
+
+    @Mock lateinit var mDreamOverlayCallbackController: DreamOverlayCallbackController
+
+    @Captor var mViewCaptor: ArgumentCaptor<View>? = null
+    var mService: DreamOverlayService? = null
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        whenever(mDreamOverlayComponent.getDreamOverlayContainerViewController())
+            .thenReturn(mDreamOverlayContainerViewController)
+        whenever(mComplicationComponent.getComplicationHostViewController())
+            .thenReturn(mComplicationHostViewController)
+        whenever(mLifecycleOwner.registry).thenReturn(mLifecycleRegistry)
+        whenever(mComplicationComponentFactory.create(any(), any(), any(), any()))
+            .thenReturn(mComplicationComponent)
+        whenever(mComplicationComponent.getVisibilityController())
+            .thenReturn(mComplicationVisibilityController)
+        whenever(mDreamComplicationComponent.getHideComplicationTouchHandler())
+            .thenReturn(mHideComplicationTouchHandler)
+        whenever(mDreamComplicationComponentFactory.create(any(), any()))
+            .thenReturn(mDreamComplicationComponent)
+        whenever(mDreamOverlayComponentFactory.create(any(), any(), any()))
+            .thenReturn(mDreamOverlayComponent)
+        whenever(mAmbientTouchComponentFactory.create(any(), any()))
+            .thenReturn(mAmbientTouchComponent)
+        whenever(mAmbientTouchComponent.getTouchMonitor()).thenReturn(mTouchMonitor)
+        whenever(mDreamOverlayContainerViewController.containerView)
+            .thenReturn(mDreamOverlayContainerView)
+        mWindowParams = WindowManager.LayoutParams()
+        mService =
+            DreamOverlayService(
+                mContext,
+                mLifecycleOwner,
+                mMainExecutor,
+                mWindowManager,
+                mComplicationComponentFactory,
+                mDreamComplicationComponentFactory,
+                mDreamOverlayComponentFactory,
+                mAmbientTouchComponentFactory,
+                mStateController,
+                mKeyguardUpdateMonitor,
+                mUiEventLogger,
+                mTouchInsetManager,
+                LOW_LIGHT_COMPONENT,
+                HOME_CONTROL_PANEL_DREAM_COMPONENT,
+                mDreamOverlayCallbackController,
+                WINDOW_NAME
+            )
+    }
+
+    @get:Throws(RemoteException::class)
+    val client: IDreamOverlayClient
+        get() {
+            val proxy = mService!!.onBind(Intent())
+            val overlay = IDreamOverlay.Stub.asInterface(proxy)
+            val callback = Mockito.mock(IDreamOverlayClientCallback::class.java)
+            overlay.getClient(callback)
+            val clientCaptor = ArgumentCaptor.forClass(IDreamOverlayClient::class.java)
+            Mockito.verify(callback).onDreamOverlayClient(clientCaptor.capture())
+            return clientCaptor.value
+        }
+
+    @Test
+    fun testOnStartMetricsLogged() {
+        val client = client
+
+        // Inform the overlay service of dream starting.
+        client.startDream(
+            mWindowParams,
+            mDreamOverlayCallback,
+            DREAM_COMPONENT,
+            false /*shouldShowComplication*/
+        )
+        mMainExecutor.runAllReady()
+        Mockito.verify(mUiEventLogger)
+            .log(DreamOverlayService.DreamOverlayEvent.DREAM_OVERLAY_ENTER_START)
+        Mockito.verify(mUiEventLogger)
+            .log(DreamOverlayService.DreamOverlayEvent.DREAM_OVERLAY_COMPLETE_START)
+    }
+
+    @Test
+    fun testOverlayContainerViewAddedToWindow() {
+        val client = client
+
+        // Inform the overlay service of dream starting.
+        client.startDream(
+            mWindowParams,
+            mDreamOverlayCallback,
+            DREAM_COMPONENT,
+            false /*shouldShowComplication*/
+        )
+        mMainExecutor.runAllReady()
+        Mockito.verify(mWindowManager).addView(any(), any())
+    }
+
+    // Validates that {@link DreamOverlayService} properly handles the case where the dream's
+    // window is no longer valid by the time start is called.
+    @Test
+    fun testInvalidWindowAddStart() {
+        val client = client
+        Mockito.doThrow(WindowManager.BadTokenException())
+            .`when`(mWindowManager)
+            .addView(any(), any())
+        // Inform the overlay service of dream starting.
+        client.startDream(
+            mWindowParams,
+            mDreamOverlayCallback,
+            DREAM_COMPONENT,
+            false /*shouldShowComplication*/
+        )
+        mMainExecutor.runAllReady()
+        Mockito.verify(mWindowManager).addView(any(), any())
+        Mockito.verify(mStateController).setOverlayActive(false)
+        Mockito.verify(mStateController).setLowLightActive(false)
+        Mockito.verify(mStateController).setEntryAnimationsFinished(false)
+        Mockito.verify(mStateController, Mockito.never()).setOverlayActive(true)
+        Mockito.verify(mUiEventLogger, Mockito.never())
+            .log(DreamOverlayService.DreamOverlayEvent.DREAM_OVERLAY_COMPLETE_START)
+        Mockito.verify(mDreamOverlayCallbackController, Mockito.never()).onStartDream()
+    }
+
+    @Test
+    fun testDreamOverlayContainerViewControllerInitialized() {
+        val client = client
+
+        // Inform the overlay service of dream starting.
+        client.startDream(
+            mWindowParams,
+            mDreamOverlayCallback,
+            DREAM_COMPONENT,
+            false /*shouldShowComplication*/
+        )
+        mMainExecutor.runAllReady()
+        Mockito.verify(mDreamOverlayContainerViewController).init()
+    }
+
+    @Test
+    fun testDreamOverlayContainerViewRemovedFromOldParentWhenInitialized() {
+        whenever(mDreamOverlayContainerView.parent)
+            .thenReturn(mDreamOverlayContainerViewParent)
+            .thenReturn(null)
+        val client = client
+
+        // Inform the overlay service of dream starting.
+        client.startDream(
+            mWindowParams,
+            mDreamOverlayCallback,
+            DREAM_COMPONENT,
+            false /*shouldShowComplication*/
+        )
+        mMainExecutor.runAllReady()
+        Mockito.verify(mDreamOverlayContainerViewParent).removeView(mDreamOverlayContainerView)
+    }
+
+    @Test
+    fun testShouldShowComplicationsSetByStartDream() {
+        val client = client
+
+        // Inform the overlay service of dream starting.
+        client.startDream(
+            mWindowParams,
+            mDreamOverlayCallback,
+            DREAM_COMPONENT,
+            true /*shouldShowComplication*/
+        )
+        mMainExecutor.runAllReady()
+        Truth.assertThat(mService!!.shouldShowComplications()).isTrue()
+    }
+
+    @Test
+    fun testLowLightSetByStartDream() {
+        val client = client
+
+        // Inform the overlay service of dream starting.
+        client.startDream(
+            mWindowParams,
+            mDreamOverlayCallback,
+            LOW_LIGHT_COMPONENT.flattenToString(),
+            false /*shouldShowComplication*/
+        )
+        mMainExecutor.runAllReady()
+        Truth.assertThat(mService!!.dreamComponent).isEqualTo(LOW_LIGHT_COMPONENT)
+        Mockito.verify(mStateController).setLowLightActive(true)
+    }
+
+    @Test
+    fun testHomeControlPanelSetsByStartDream() {
+        val client = client
+
+        // Inform the overlay service of dream starting.
+        client.startDream(
+            mWindowParams,
+            mDreamOverlayCallback,
+            HOME_CONTROL_PANEL_DREAM_COMPONENT.flattenToString(),
+            false /*shouldShowComplication*/
+        )
+        mMainExecutor.runAllReady()
+        Truth.assertThat(mService!!.dreamComponent).isEqualTo(HOME_CONTROL_PANEL_DREAM_COMPONENT)
+        Mockito.verify(mStateController).setHomeControlPanelActive(true)
+    }
+
+    @Test
+    fun testOnEndDream() {
+        val client = client
+
+        // Inform the overlay service of dream starting.
+        client.startDream(
+            mWindowParams,
+            mDreamOverlayCallback,
+            LOW_LIGHT_COMPONENT.flattenToString(),
+            false /*shouldShowComplication*/
+        )
+        mMainExecutor.runAllReady()
+
+        // Verify view added.
+        Mockito.verify(mWindowManager).addView(mViewCaptor!!.capture(), any())
+
+        // Service destroyed.
+        mService!!.onEndDream()
+        mMainExecutor.runAllReady()
+
+        // Verify view removed.
+        Mockito.verify(mWindowManager).removeView(mViewCaptor!!.value)
+
+        // Verify state correctly set.
+        Mockito.verify(mStateController).setOverlayActive(false)
+        Mockito.verify(mStateController).setLowLightActive(false)
+        Mockito.verify(mStateController).setEntryAnimationsFinished(false)
+    }
+
+    @Test
+    fun testImmediateEndDream() {
+        val client = client
+
+        // Start the dream, but don't execute any Runnables put on the executor yet. We delay
+        // executing Runnables as the timing isn't guaranteed and we want to verify that the overlay
+        // starts and finishes in the proper order even if Runnables are delayed.
+        client.startDream(
+            mWindowParams,
+            mDreamOverlayCallback,
+            DREAM_COMPONENT,
+            false /*shouldShowComplication*/
+        )
+        // Immediately end the dream.
+        client.endDream()
+        // Run any scheduled Runnables.
+        mMainExecutor.runAllReady()
+
+        // The overlay starts then finishes.
+        val inOrder = Mockito.inOrder(mWindowManager)
+        inOrder.verify(mWindowManager).addView(mViewCaptor!!.capture(), any())
+        inOrder.verify(mWindowManager).removeView(mViewCaptor!!.value)
+    }
+
+    @Test
+    fun testEndDreamDuringStartDream() {
+        val client = client
+
+        // Schedule the endDream call in the middle of the startDream implementation, as any
+        // ordering is possible.
+        Mockito.doAnswer { invocation: InvocationOnMock? ->
+                client.endDream()
+                null
+            }
+            .`when`(mStateController)
+            .setOverlayActive(true)
+
+        // Start the dream.
+        client.startDream(
+            mWindowParams,
+            mDreamOverlayCallback,
+            DREAM_COMPONENT,
+            false /*shouldShowComplication*/
+        )
+        mMainExecutor.runAllReady()
+
+        // The overlay starts then finishes.
+        val inOrder = Mockito.inOrder(mWindowManager)
+        inOrder.verify(mWindowManager).addView(mViewCaptor!!.capture(), any())
+        inOrder.verify(mWindowManager).removeView(mViewCaptor!!.value)
+    }
+
+    @Test
+    fun testDestroy() {
+        val client = client
+
+        // Inform the overlay service of dream starting.
+        client.startDream(
+            mWindowParams,
+            mDreamOverlayCallback,
+            LOW_LIGHT_COMPONENT.flattenToString(),
+            false /*shouldShowComplication*/
+        )
+        mMainExecutor.runAllReady()
+
+        // Verify view added.
+        Mockito.verify(mWindowManager).addView(mViewCaptor!!.capture(), any())
+
+        // Service destroyed.
+        mService!!.onDestroy()
+        mMainExecutor.runAllReady()
+
+        // Verify view removed.
+        Mockito.verify(mWindowManager).removeView(mViewCaptor!!.value)
+
+        // Verify state correctly set.
+        Mockito.verify(mKeyguardUpdateMonitor).removeCallback(any())
+        Mockito.verify(mLifecycleRegistry).currentState = Lifecycle.State.DESTROYED
+        Mockito.verify(mStateController).setOverlayActive(false)
+        Mockito.verify(mStateController).setLowLightActive(false)
+        Mockito.verify(mStateController).setEntryAnimationsFinished(false)
+    }
+
+    @Test
+    fun testDoNotRemoveViewOnDestroyIfOverlayNotStarted() {
+        // Service destroyed without ever starting dream.
+        mService!!.onDestroy()
+        mMainExecutor.runAllReady()
+
+        // Verify no view is removed.
+        Mockito.verify(mWindowManager, Mockito.never()).removeView(any())
+
+        // Verify state still correctly set.
+        Mockito.verify(mKeyguardUpdateMonitor).removeCallback(any())
+        Mockito.verify(mLifecycleRegistry).currentState = Lifecycle.State.DESTROYED
+        Mockito.verify(mStateController).setOverlayActive(false)
+        Mockito.verify(mStateController).setLowLightActive(false)
+    }
+
+    @Test
+    fun testDecorViewNotAddedToWindowAfterDestroy() {
+        val client = client
+
+        // Destroy the service.
+        mService!!.onDestroy()
+        mMainExecutor.runAllReady()
+
+        // Inform the overlay service of dream starting.
+        client.startDream(
+            mWindowParams,
+            mDreamOverlayCallback,
+            DREAM_COMPONENT,
+            false /*shouldShowComplication*/
+        )
+        mMainExecutor.runAllReady()
+        Mockito.verify(mWindowManager, Mockito.never()).addView(any(), any())
+    }
+
+    @Test
+    fun testNeverRemoveDecorViewIfNotAdded() {
+        // Service destroyed before dream started.
+        mService!!.onDestroy()
+        mMainExecutor.runAllReady()
+        Mockito.verify(mWindowManager, Mockito.never()).removeView(any())
+    }
+
+    @Test
+    fun testResetCurrentOverlayWhenConnectedToNewDream() {
+        val client = client
+
+        // Inform the overlay service of dream starting. Do not show dream complications.
+        client.startDream(
+            mWindowParams,
+            mDreamOverlayCallback,
+            DREAM_COMPONENT,
+            false /*shouldShowComplication*/
+        )
+        mMainExecutor.runAllReady()
+
+        // Verify that a new window is added.
+        Mockito.verify(mWindowManager).addView(mViewCaptor!!.capture(), any())
+        val windowDecorView = mViewCaptor!!.value
+
+        // Assert that the overlay is not showing complications.
+        Truth.assertThat(mService!!.shouldShowComplications()).isFalse()
+        Mockito.clearInvocations(mDreamOverlayComponent)
+        Mockito.clearInvocations(mAmbientTouchComponent)
+        Mockito.clearInvocations(mWindowManager)
+
+        // New dream starting with dream complications showing. Note that when a new dream is
+        // binding to the dream overlay service, it receives the same instance of IBinder as the
+        // first one.
+        client.startDream(
+            mWindowParams,
+            mDreamOverlayCallback,
+            DREAM_COMPONENT,
+            true /*shouldShowComplication*/
+        )
+        mMainExecutor.runAllReady()
+
+        // Assert that the overlay is showing complications.
+        Truth.assertThat(mService!!.shouldShowComplications()).isTrue()
+
+        // Verify that the old overlay window has been removed, and a new one created.
+        Mockito.verify(mWindowManager).removeView(windowDecorView)
+        Mockito.verify(mWindowManager).addView(any(), any())
+
+        // Verify that new instances of overlay container view controller and overlay touch monitor
+        // are created.
+        Mockito.verify(mDreamOverlayComponent).getDreamOverlayContainerViewController()
+        Mockito.verify(mAmbientTouchComponent).getTouchMonitor()
+    }
+
+    @Test
+    fun testWakeUp() {
+        val client = client
+
+        // Inform the overlay service of dream starting.
+        client.startDream(
+            mWindowParams,
+            mDreamOverlayCallback,
+            DREAM_COMPONENT,
+            true /*shouldShowComplication*/
+        )
+        mMainExecutor.runAllReady()
+        mService!!.onWakeUp()
+        Mockito.verify(mDreamOverlayContainerViewController).wakeUp()
+        Mockito.verify(mDreamOverlayCallbackController).onWakeUp()
+    }
+
+    @Test
+    fun testWakeUpBeforeStartDoesNothing() {
+        mService!!.onWakeUp()
+        Mockito.verify(mDreamOverlayContainerViewController, Mockito.never()).wakeUp()
+    }
+
+    @Test
+    fun testSystemFlagShowForAllUsersSetOnWindow() {
+        val client = client
+
+        // Inform the overlay service of dream starting. Do not show dream complications.
+        client.startDream(
+            mWindowParams,
+            mDreamOverlayCallback,
+            DREAM_COMPONENT,
+            false /*shouldShowComplication*/
+        )
+        mMainExecutor.runAllReady()
+        val paramsCaptor = ArgumentCaptor.forClass(WindowManager.LayoutParams::class.java)
+
+        // Verify that a new window is added.
+        Mockito.verify(mWindowManager).addView(any(), paramsCaptor.capture())
+        Truth.assertThat(
+                paramsCaptor.value.privateFlags and
+                    WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS ==
+                    WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS
+            )
+            .isTrue()
+    }
+
+    companion object {
+        private val LOW_LIGHT_COMPONENT = ComponentName("package", "lowlight")
+        private val HOME_CONTROL_PANEL_DREAM_COMPONENT =
+            ComponentName("package", "homeControlPanel")
+        private const val DREAM_COMPONENT = "package/dream"
+        private const val WINDOW_NAME = "test"
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 470d342..65fd101 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -85,6 +85,7 @@
 import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor
 import com.android.systemui.telephony.data.repository.fakeTelephonyRepository
 import com.android.systemui.testKosmos
+import com.android.systemui.unfold.domain.interactor.unfoldTransitionInteractor
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
@@ -228,6 +229,7 @@
                 footerActionsController = kosmos.footerActionsController,
                 footerActionsViewModelFactory = kosmos.footerActionsViewModelFactory,
                 sceneInteractor = sceneInteractor,
+                unfoldTransitionInteractor = kosmos.unfoldTransitionInteractor,
             )
 
         val displayTracker = FakeDisplayTracker(context)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
index ab95e2c..2397de6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
@@ -24,6 +24,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
@@ -44,10 +45,14 @@
 import com.android.systemui.shade.shared.model.ShadeMode
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel
 import com.android.systemui.testKosmos
+import com.android.systemui.unfold.domain.interactor.unfoldTransitionInteractor
+import com.android.systemui.unfold.fakeUnfoldTransitionProgressProvider
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
+import java.util.Locale
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -91,6 +96,7 @@
                 footerActionsViewModelFactory = kosmos.footerActionsViewModelFactory,
                 footerActionsController = kosmos.footerActionsController,
                 sceneInteractor = kosmos.sceneInteractor,
+                unfoldTransitionInteractor = kosmos.unfoldTransitionInteractor,
             )
     }
 
@@ -254,4 +260,59 @@
             shadeRepository.setShadeMode(ShadeMode.Split)
             assertThat(shadeMode).isEqualTo(ShadeMode.Split)
         }
+
+    @Test
+    fun unfoldTransitionProgress() =
+        testScope.runTest {
+            val maxTranslation = prepareConfiguration()
+            val translations by
+                collectLastValue(
+                    combine(
+                        underTest.unfoldTranslationX(isOnStartSide = true),
+                        underTest.unfoldTranslationX(isOnStartSide = false),
+                    ) { start, end ->
+                        Translations(
+                            start = start,
+                            end = end,
+                        )
+                    }
+                )
+
+            val unfoldProvider = kosmos.fakeUnfoldTransitionProgressProvider
+            unfoldProvider.onTransitionStarted()
+            assertThat(translations?.start).isEqualTo(0f)
+            assertThat(translations?.end).isEqualTo(-0f)
+
+            repeat(10) { repetition ->
+                val transitionProgress = 0.1f * (repetition + 1)
+                unfoldProvider.onTransitionProgress(transitionProgress)
+                assertThat(translations?.start).isEqualTo((1 - transitionProgress) * maxTranslation)
+                assertThat(translations?.end).isEqualTo(-(1 - transitionProgress) * maxTranslation)
+            }
+
+            unfoldProvider.onTransitionFinishing()
+            assertThat(translations?.start).isEqualTo(0f)
+            assertThat(translations?.end).isEqualTo(-0f)
+
+            unfoldProvider.onTransitionFinished()
+            assertThat(translations?.start).isEqualTo(0f)
+            assertThat(translations?.end).isEqualTo(-0f)
+        }
+
+    private fun prepareConfiguration(): Int {
+        val configuration = context.resources.configuration
+        configuration.setLayoutDirection(Locale.US)
+        kosmos.fakeConfigurationRepository.onConfigurationChange(configuration)
+        val maxTranslation = 10
+        kosmos.fakeConfigurationRepository.setDimensionPixelSize(
+            R.dimen.notification_side_paddings,
+            maxTranslation
+        )
+        return maxTranslation
+    }
+
+    private data class Translations(
+        val start: Float,
+        val end: Float,
+    )
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractorTest.kt
new file mode 100644
index 0000000..12f08a3
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractorTest.kt
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.systemui.unfold.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.res.R
+import com.android.systemui.testKosmos
+import com.android.systemui.unfold.fakeUnfoldTransitionProgressProvider
+import com.google.common.truth.Truth.assertThat
+import java.util.Locale
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.async
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class UnfoldTransitionInteractorTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val unfoldTransitionProgressProvider = kosmos.fakeUnfoldTransitionProgressProvider
+
+    private val underTest: UnfoldTransitionInteractor = kosmos.unfoldTransitionInteractor
+
+    @Test
+    fun waitForTransitionFinish_noEvents_doesNotComplete() =
+        testScope.runTest {
+            val deferred = async { underTest.waitForTransitionFinish() }
+
+            runCurrent()
+
+            assertThat(deferred.isCompleted).isFalse()
+            deferred.cancel()
+        }
+
+    @Test
+    fun waitForTransitionFinish_finishEvent_completes() =
+        testScope.runTest {
+            val deferred = async { underTest.waitForTransitionFinish() }
+
+            runCurrent()
+            unfoldTransitionProgressProvider.onTransitionFinished()
+            runCurrent()
+
+            assertThat(deferred.isCompleted).isTrue()
+            deferred.cancel()
+        }
+
+    @Test
+    fun waitForTransitionFinish_otherEvent_doesNotComplete() =
+        testScope.runTest {
+            val deferred = async { underTest.waitForTransitionFinish() }
+
+            runCurrent()
+            unfoldTransitionProgressProvider.onTransitionStarted()
+            runCurrent()
+
+            assertThat(deferred.isCompleted).isFalse()
+            deferred.cancel()
+        }
+
+    @Test
+    fun unfoldTranslationX_leftToRight() =
+        testScope.runTest {
+            val maxTranslation = prepareConfiguration(isLeftToRight = true)
+            val translations by
+                collectLastValue(
+                    combine(
+                        underTest.unfoldTranslationX(isOnStartSide = true),
+                        underTest.unfoldTranslationX(isOnStartSide = false),
+                    ) { start, end ->
+                        Translations(
+                            start = start,
+                            end = end,
+                        )
+                    }
+                )
+            runCurrent()
+
+            unfoldTransitionProgressProvider.onTransitionStarted()
+            assertThat(translations?.start).isEqualTo(0f)
+            assertThat(translations?.end).isEqualTo(-0f)
+
+            repeat(10) { repetition ->
+                val transitionProgress = 1 - 0.1f * (repetition + 1)
+                unfoldTransitionProgressProvider.onTransitionProgress(transitionProgress)
+                assertThat(translations?.start).isEqualTo((1 - transitionProgress) * maxTranslation)
+                assertThat(translations?.end).isEqualTo(-(1 - transitionProgress) * maxTranslation)
+            }
+
+            unfoldTransitionProgressProvider.onTransitionFinishing()
+            assertThat(translations?.start).isEqualTo(maxTranslation)
+            assertThat(translations?.end).isEqualTo(-maxTranslation)
+
+            unfoldTransitionProgressProvider.onTransitionFinished()
+            assertThat(translations?.start).isEqualTo(0f)
+            assertThat(translations?.end).isEqualTo(-0f)
+        }
+
+    @Test
+    fun unfoldTranslationX_rightToLeft() =
+        testScope.runTest {
+            val maxTranslation = prepareConfiguration(isLeftToRight = false)
+            val translations by
+                collectLastValue(
+                    combine(
+                        underTest.unfoldTranslationX(isOnStartSide = true),
+                        underTest.unfoldTranslationX(isOnStartSide = false),
+                    ) { start, end ->
+                        Translations(
+                            start = start,
+                            end = end,
+                        )
+                    }
+                )
+            runCurrent()
+
+            unfoldTransitionProgressProvider.onTransitionStarted()
+            assertThat(translations?.start).isEqualTo(-0f)
+            assertThat(translations?.end).isEqualTo(0f)
+
+            repeat(10) { repetition ->
+                val transitionProgress = 1 - 0.1f * (repetition + 1)
+                unfoldTransitionProgressProvider.onTransitionProgress(transitionProgress)
+                assertThat(translations?.start)
+                    .isEqualTo(-(1 - transitionProgress) * maxTranslation)
+                assertThat(translations?.end).isEqualTo((1 - transitionProgress) * maxTranslation)
+            }
+
+            unfoldTransitionProgressProvider.onTransitionFinishing()
+            assertThat(translations?.start).isEqualTo(-maxTranslation)
+            assertThat(translations?.end).isEqualTo(maxTranslation)
+
+            unfoldTransitionProgressProvider.onTransitionFinished()
+            assertThat(translations?.start).isEqualTo(-0f)
+            assertThat(translations?.end).isEqualTo(0f)
+        }
+
+    private fun prepareConfiguration(
+        isLeftToRight: Boolean,
+    ): Int {
+        val configuration = context.resources.configuration
+        if (isLeftToRight) {
+            configuration.setLayoutDirection(Locale.US)
+        } else {
+            configuration.setLayoutDirection(Locale("he", "il"))
+        }
+        kosmos.fakeConfigurationRepository.onConfigurationChange(configuration)
+        val maxTranslation = 10
+        kosmos.fakeConfigurationRepository.setDimensionPixelSize(
+            R.dimen.notification_side_paddings,
+            maxTranslation
+        )
+        return maxTranslation
+    }
+
+    private data class Translations(
+        val start: Float,
+        val end: Float,
+    )
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModelTest.kt
index fdeded8..4cf924a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModelTest.kt
@@ -64,7 +64,7 @@
                 val buttonViewModel by collectLastValue(underTest.buttonViewModel)
                 runCurrent()
 
-                assertThat(buttonViewModel!!.isChecked).isFalse()
+                assertThat(buttonViewModel!!.isActive).isFalse()
             }
         }
     }
@@ -78,7 +78,7 @@
                 val buttonViewModel by collectLastValue(underTest.buttonViewModel)
                 runCurrent()
 
-                assertThat(buttonViewModel!!.isChecked).isTrue()
+                assertThat(buttonViewModel!!.isActive).isTrue()
             }
         }
     }
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
index 8e2bd9b..79bf5f1 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
@@ -267,6 +267,9 @@
 
     /** True if the clock will react to tone changes in the seed color. */
     val isReactiveToTone: Boolean = true,
+
+    /** True if the clock is large frame clock, which will use weather in compose. */
+    val useCustomClockScene: Boolean = false,
 )
 
 /** Render configuration options for a clock face. Modifies the way SystemUI behaves. */
@@ -283,6 +286,9 @@
      * animation will be used (e.g. a simple translation).
      */
     val hasCustomPositionUpdatedAnimation: Boolean = false,
+
+    /** True if the clock is large frame clock, which will use weatherBlueprint in compose. */
+    val useCustomClockScene: Boolean = false,
 )
 
 /** Structure for keeping clock-specific settings */
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index ccea0fc..0e1bed8 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Sien alles"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Gebruik Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Gekoppel"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gestoor"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ontkoppel"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiveer"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Skakel dit môre outomaties weer aan"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Kenmerke soos Kitsdeel, Kry My Toestel en toestelligging gebruik Bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth sal môre om 05:00 aanskakel"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batterykrag"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Oudio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Kopstuk"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 9e528e0c..3709a900 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"ሁሉንም ይመልከቱ"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"ብሉቱዝን ይጠቀሙ"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"ተገናኝቷል"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ተቀምጧል"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ግንኙነትን አቋርጥ"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ያግብሩ"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ነገ እንደገና በራስ-ሰር አስጀምር"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"እንደ ፈጣን ማጋራት፣ የእኔን መሣሪያ አግኝ እና የመሣሪያ አካባቢ ያሉ ባህሪያት ብሉቱዝን ይጠቀማሉ"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"ብሉቱዝ ነገ ጠዋቱ 5 ሰዓት ላይ ይበራል"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ባትሪ"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ኦዲዮ"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ማዳመጫ"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 759f957..0a7250a 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -75,7 +75,7 @@
     <string name="global_action_smart_lock_disabled" msgid="6286551337177954859">"تم إيقاف ميزة \"إبقاء الجهاز مفتوحًا\"."</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"أرسَل صورة"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"جارٍ حفظ لقطة الشاشة..."</string>
-    <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"جارٍ حفظ لقطة الشاشة في الملف الشخصي للعمل…"</string>
+    <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"جارٍ حفظ لقطة الشاشة في ملف العمل…"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"تم حفظ لقطة الشاشة."</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"تعذّر حفظ لقطة الشاشة"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"الشاشة الخارجية"</string>
@@ -90,13 +90,13 @@
     <string name="screenshot_share_description" msgid="2861628935812656612">"مشاركة لقطة الشاشة"</string>
     <string name="screenshot_scroll_label" msgid="2930198809899329367">"التقاط المزيد من المحتوى"</string>
     <string name="screenshot_dismiss_description" msgid="4702341245899508786">"إغلاق لقطة الشاشة"</string>
-    <string name="screenshot_dismiss_work_profile" msgid="3101530842987697045">"تجاهل رسالة الملف الشخصي للعمل"</string>
+    <string name="screenshot_dismiss_work_profile" msgid="3101530842987697045">"تجاهل رسالة ملف العمل"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"معاينة لقطة الشاشة"</string>
     <string name="screenshot_top_boundary_pct" msgid="2520148599096479332">"الحد العلوي <xliff:g id="PERCENT">%1$d</xliff:g> في المئة"</string>
     <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"الحد السفلى <xliff:g id="PERCENT">%1$d</xliff:g> في المئة"</string>
     <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"الحد الأيسر <xliff:g id="PERCENT">%1$d</xliff:g> في المئة"</string>
     <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"الحد الأيمن <xliff:g id="PERCENT">%1$d</xliff:g> في المئة"</string>
-    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"تم حفظ لقطة الشاشة في \"<xliff:g id="APP">%1$s</xliff:g>\" في الملف الشخصي للعمل."</string>
+    <string name="screenshot_work_profile_notification" msgid="203041724052970693">"تم حفظ لقطة الشاشة في \"<xliff:g id="APP">%1$s</xliff:g>\" في ملف العمل."</string>
     <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"الملفات"</string>
     <string name="screenshot_detected_template" msgid="7940376642921719915">"رصَد تطبيق \"<xliff:g id="APPNAME">%1$s</xliff:g>\" لقطة الشاشة هذه."</string>
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"رصَد تطبيق \"<xliff:g id="APPNAME">%1$s</xliff:g>\" والتطبيقات المفتوحة الأخرى لقطة الشاشة هذه."</string>
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"عرض الكل"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"استخدام البلوتوث"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"متّصل"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"محفوظ"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"إلغاء الربط"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"تفعيل"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"تفعيل البلوتوث تلقائيًا مرة أخرى غدًا"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"‏يُستخدَم البلوتوث في ميزات مثل Quick Share و\"العثور على جهازي\" والموقع الجغرافي للجهاز"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"سيتم تفعيل البلوتوث غدًا الساعة 5 صباحًا"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"مستوى طاقة البطارية <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"صوت"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"سماعة الرأس"</string>
@@ -515,9 +523,9 @@
     <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"هذا الجهاز يخص <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>."</string>
     <string name="quick_settings_disclosure_management_vpns" msgid="929181757984262902">"‏ينتمي هذا الجهاز إلى مؤسستك، ويتّصل بالإنترنت من خلال خدمات الشبكة الافتراضية الخاصة (VPN)."</string>
     <string name="quick_settings_disclosure_named_management_vpns" msgid="3312645578322079185">"‏ينتمي هذا الجهاز إلى <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>، ويتّصل بالإنترنت من خلال خدمات الشبكة الافتراضية الخاصة (VPN)."</string>
-    <string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"يمكن لمؤسستك مراقبة حركة بيانات الشبكة في الملف الشخصي للعمل"</string>
+    <string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"يمكن لمؤسستك مراقبة حركة بيانات الشبكة في ملف العمل"</string>
     <string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"يمكن لـ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> مراقبة حركة بيانات الشبكة في ملفك الشخصي للعمل"</string>
-    <string name="quick_settings_disclosure_managed_profile_network_activity" msgid="2636594621387832827">"تكون أنشطة شبكة الملف الشخصي للعمل مرئية لمشرف تكنولوجيا المعلومات."</string>
+    <string name="quick_settings_disclosure_managed_profile_network_activity" msgid="2636594621387832827">"تكون أنشطة شبكة ملف العمل مرئية لمشرف تكنولوجيا المعلومات."</string>
     <string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"قد تكون الشبكة خاضعة للمراقبة"</string>
     <string name="quick_settings_disclosure_vpns" msgid="3586175303518266301">"‏هذا الجهاز متّصل بالإنترنت من خلال خدمات الشبكات الافتراضية الخاصة (VPN)."</string>
     <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="153393105176944100">"تطبيقات العمل الخاصة بك متّصلة بالإنترنت من خلال <xliff:g id="VPN_APP">%1$s</xliff:g>."</string>
@@ -636,7 +644,7 @@
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"إعدادات شاشة القفل"</string>
     <string name="qr_code_scanner_title" msgid="1938155688725760702">"ماسح ضوئي لرمز الاستجابة السريعة"</string>
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"جارٍ تعديل الحالة"</string>
-    <string name="status_bar_work" msgid="5238641949837091056">"الملف الشخصي للعمل"</string>
+    <string name="status_bar_work" msgid="5238641949837091056">"ملف العمل"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"وضع الطيران"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"لن تسمع المنبّه القادم في <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template" msgid="2234991538018805736">"في <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -646,7 +654,7 @@
     <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"قمر صناعي، الاتصال ضعيف"</string>
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"قمر صناعي، الاتصال جيد"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"قمر صناعي، الاتصال متوفّر"</string>
-    <string name="accessibility_managed_profile" msgid="4703836746209377356">"الملف الشخصي للعمل"</string>
+    <string name="accessibility_managed_profile" msgid="4703836746209377356">"ملف العمل"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"متعة للبعض وليس للجميع"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"‏توفر لك أداة ضبط واجهة مستخدم النظام طرقًا إضافية لتعديل واجهة مستخدم Android وتخصيصها. ويمكن أن تطرأ تغييرات على هذه الميزات التجريبية أو يمكن أن تتعطل هذه الميزات أو تختفي في الإصدارات المستقبلية. عليك متابعة الاستخدام مع توخي الحذر."</string>
     <string name="tuner_persistent_warning" msgid="230466285569307806">"يمكن أن تطرأ تغييرات على هذه الميزات التجريبية أو يمكن أن تتعطل هذه الميزات أو تختفي في الإصدارات المستقبلية. عليك متابعة الاستخدام مع توخي الحذر."</string>
@@ -1262,8 +1270,8 @@
     <string name="video_camera" msgid="7654002575156149298">"كاميرا فيديو"</string>
     <string name="call_from_work_profile_title" msgid="5418253516453177114">"لا يمكن الاتصال من تطبيق شخصي"</string>
     <string name="call_from_work_profile_text" msgid="2856337395968118274">"تسمح لك مؤسستك بإجراء المكالمات من تطبيقات العمل فقط."</string>
-    <string name="call_from_work_profile_action" msgid="2937701298133010724">"التبديل إلى الملف الشخصي للعمل"</string>
-    <string name="install_dialer_on_work_profile_action" msgid="2014659711597862506">"تثبيت تطبيق الهاتف في الملف الشخصي للعمل"</string>
+    <string name="call_from_work_profile_action" msgid="2937701298133010724">"التبديل إلى ملف العمل"</string>
+    <string name="install_dialer_on_work_profile_action" msgid="2014659711597862506">"تثبيت تطبيق الهاتف في ملف العمل"</string>
     <string name="call_from_work_profile_close" msgid="5830072964434474143">"إلغاء"</string>
     <string name="lock_screen_settings" msgid="6152703934761402399">"تخصيص شاشة القفل"</string>
     <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"الفتح لتخصيص شاشة القفل"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 1f859ac..3311588 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"আটাইবোৰ চাওক"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"ব্লুটুথ ব্যৱহাৰ কৰক"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"সংযুক্ত আছে"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ছেভ কৰা হৈছে"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"সংযোগ বিচ্ছিন্ন কৰক"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"সক্ৰিয় কৰক"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"কাইলৈ পুনৰ স্বয়ংক্ৰিয়ভাৱে অন কৰক"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Quick Share, Find My Device আৰু ডিভাইচৰ অৱস্থানৰ দৰে সুবিধাই ব্লুটুথ ব্যৱহাৰ কৰে"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"কাইলৈ পুৱা ৫ বজাত ব্লুটুথ অন হ’ব"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"বেটাৰী <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"অডিঅ’"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"হেডছেট"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index dc095c9..741cc41 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Hamısına baxın"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth aç"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Qoşulub"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Yadda saxlandı"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"əlaqəni kəsin"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivləşdirin"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Sabah avtomatik aktiv edin"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Cəld Paylaşım, Cihazın Tapılması və cihaz məkanı kimi funksiyalar Bluetooth istifadə edir"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth sabah 05:00-da aktiv olacaq"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batareya"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Qulaqlıq"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 2f86ca4..583ffaf 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Prikaži sve"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Koristi Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Povezano"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Sačuvano"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"prekinite vezu"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivirajte"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatski ponovo uključi sutra"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Funkcije kao što su Quick Share, Pronađi moj uređaj i lokacija uređaja koriste Bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth će se uključiti sutra u 5:00"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Nivo baterije je <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalice"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index bc03d8c..50b407f 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Паглядзець усе"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Выкарыстоўваць Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Падключана"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Захавана"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"адключыць"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"актываваць"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Аўтаматычнае ўключэнне заўтра"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Bluetooth выкарыстоўваецца для вызначэння месцазнаходжання прылады, а таксама такімі функцыямі, як Хуткае абагульванне і Знайсці прыладу"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth будзе ўключаны заўтра ў 5 гадзін раніцы"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Узровень зараду: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Гук"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнітура"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Запіс праблемы"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Пачынайце"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Спыніцеся"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Справаздача"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"З чым была звязана праблема, якая вам сустрэлася?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Выберыце тып праблемы"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Запіс экрана"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index d80b889..3a28329 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Преглед на всички"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Използване на Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Установена е връзка"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Запазено"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"прекратяване на връзката"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"активиране"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Автоматично включване отново утре"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Bluetooth се използва от различни функции, като например „Бързо споделяне“, „Намиране на устройството ми“ и местоположението на устройството"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth ще се включи утре в 5:00 ч."</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батерия: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Слушалки"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Записване на проблем"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Стартиране"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Спиране"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Сигнал за грешка"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"С какво имахте проблем?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Изберете тип проблем"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Запис на екрана"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 36b57ee..ec704b8 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"সব দেখুন"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"ব্লুটুথ ব্যবহার করুন"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"কানেক্ট করা আছে"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"সেভ করা আছে"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ডিসকানেক্ট করুন"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"চালু করুন"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"আগামীকাল অটোমেটিক আবার চালু হবে"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"\'দ্রুত শেয়ার\', \'Find My Device\' ও \'ডিভাইস লোকেশনের\' মতো ফিচার ব্লুটুথ ব্যবহার করে"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"আগামীকাল ভোর ৫টায় ব্লুটুথ চালু হবে"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"চার্জ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"অডিও"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"হেডসেট"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"রেকর্ডিংয়ে সমস্যা"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"শুরু করুন"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"বন্ধ করুন"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"সমস্যার রিপোর্ট"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ডিভাইস ব্যবহার করার সময় কোথায় অসুবিধা হয়েছিল?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"সমস্যার প্রকার বেছে নিন"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"স্ক্রিন রেকর্ড"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 91acfa5..fd77f14 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Prikaži sve"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Koristi Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Povezano"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Sačuvano"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"prekid veze"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiviranje"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatski uključi ponovo sutra"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Funkcije kao što su Quick Share, Pronađi moj uređaj i lokacija uređaja koriste Bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth će se uključiti sutra u 5:00"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> baterije"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvuk"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalice"</string>
@@ -350,7 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Snimite problem"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Pokrenite"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Zaustavite"</string>
-    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Izvješće o pogrešci"</string>
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Izvještaj o grešci"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Koji dio uređaja je imao problem?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Odaberite vrstu problema"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Snimanje ekrana"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 1445623..aeb0ef0 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Mostra-ho tot"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Utilitza\'l"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connectat"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Desat"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconnecta"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activa"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Torna\'l a activar automàticament demà"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Funcions com ara Quick Share, Troba el meu dispositiu i la ubicació del dispositiu utilitzen el Bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"El Bluetooth s\'activarà demà a les 5:00 h"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de bateria"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Àudio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculars"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Registra el problema"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Inicia"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Atura"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Informe d\'errors"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"L\'experiència amb el dispositiu s\'ha vist afectada?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecciona el tipus de problema"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravació de pantalla"</string>
@@ -604,7 +611,7 @@
     <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Toca per silenciar."</string>
     <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Control de soroll"</string>
     <string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"Àudio espacial"</string>
-    <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Desactiva"</string>
+    <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Desactivat"</string>
     <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fix"</string>
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Seguiment del cap"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Toca per canviar el mode de timbre"</string>
@@ -1303,5 +1310,5 @@
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroil·luminació del teclat"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivell %1$d de %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Controls de la llar"</string>
-    <string name="home_controls_dream_description" msgid="4644150952104035789">"Utilitza controls de la llar com a salvapantalles"</string>
+    <string name="home_controls_dream_description" msgid="4644150952104035789">"Utilitza controls de la llar com a estalvi de pantalla"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 7b766ab..891f836 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Zobrazit vše"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Použít Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Připojeno"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Uloženo"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"odpojit"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivovat"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Zítra znovu automaticky zapnout"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Funkce jako Quick Share, Najdi moje zařízení a vyhledávání zařízení používají Bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth se zapne zítra v 5:00"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Baterie: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvuk"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Sluchátka"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Zaznamenat problém"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Spustit"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Ukončit"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Zpráva o chybě"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Co v zařízení bylo ovlivněno?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Vyberte druh problém"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Záznam obrazovky"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 2100e27..5c721a1 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Se alt"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Brug Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Der er oprettet forbindelse"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gemt"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"afbryd forbindelse"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivér"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Aktivér automatisk igen i morgen"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Funktioner som f.eks. Quick Share, Find min enhed og enhedslokation anvender Bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth aktiveres i morgen kl. 5.00"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batteri"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Lyd"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Optag problem"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Start"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Stop"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Fejlrapport"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Hvilken del af din enhedsoplevelse blev påvirket?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Vælg problemtype"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skærmoptagelse"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 6c4f4b5..510dba9 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -198,7 +198,7 @@
     <string name="face_re_enroll_notification_title" msgid="1850838867718410520">"Entsperrung per Gesichtserkennung neu einrichten"</string>
     <string name="face_re_enroll_notification_name" msgid="7384545252206120659">"Entsperrung per Gesichtserkennung"</string>
     <string name="face_re_enroll_dialog_title" msgid="6392173708176069994">"Entsperrung per Gesichtserkennung einrichten"</string>
-    <string name="face_re_enroll_dialog_content" msgid="7353502359464038511">"Wenn du die Entsperrung per Gesichtserkennung neu einrichtest, wird dein aktuelles Gesichtsmodell gelöscht.\n\nDu musst diese Funktion neu einrichten, damit du dein Smartphone weiterhin mit deinem Gesicht entsperren kannst."</string>
+    <string name="face_re_enroll_dialog_content" msgid="7353502359464038511">"Wenn du die Entsperrung per Gesichtserkennung neu einrichtest, wird dein aktuelles Gesichtsmodell gelöscht.\n\nDu musst diese Funktion neu einrichten, damit du dein Smartphone weiterhin mithilfe der Gesichtserkennung entsperren kannst."</string>
     <string name="face_reenroll_failure_dialog_content" msgid="7073947334397236935">"Die Entsperrung per Gesichtserkennung konnte nicht eingerichtet werden. Gehe zu den Einstellungen und versuche es noch einmal."</string>
     <string name="fingerprint_dialog_touch_sensor" msgid="2817887108047658975">"Berühre den Fingerabdrucksensor"</string>
     <string name="fingerprint_dialog_authenticated_confirmation" msgid="1603899612957562862">"Tippe zum Fortfahren auf das Symbol „Entsperren“"</string>
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Alle anzeigen"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth verwenden"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Verbunden"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gespeichert"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"Verknüpfung aufheben"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivieren"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Morgen automatisch wieder aktivieren"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Für Funktionen wie Quick Share, „Mein Gerät finden“ und den Gerätestandort wird Bluetooth verwendet"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth wird morgen um 5:00 Uhr aktiviert"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akkustand: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Problem aufnehmen"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Aufnahme starten"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Aufnahme beenden"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Fehlerbericht"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Welche Bereiche des Geräts waren betroffen?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Art des Problems auswählen"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Bildschirmaufnahme"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 4c964de..a881536 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Εμφάνιση όλων"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Χρήση Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Συνδέθηκε"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Αποθηκεύτηκε"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"αποσύνδεση"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ενεργοποίηση"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Αυτόματη ενεργοποίηση ξανά αύριο"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Λειτουργίες όπως το Quick Share, η Εύρεση συσκευής και η τοποθεσία της συσκευής χρησιμοποιούν Bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Το Bluetooth θα ενεργοποιηθεί αύριο στις 5 π.μ."</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Μπαταρία <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ήχος"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ακουστικά"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Εγγραφή προβλήματος"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Έναρξη"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Διακοπή"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Αναφορά σφάλματος"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Ποιο κομμάτι της εμπειρίας συσκευής επηρεάστηκε;"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Επιλογή τύπου προβλήματος"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Εγγραφή οθόνης"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index fc27f16..d2af8a6 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"See all"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Use Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connected"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnect"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activate"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Features like Quick Share, Find My Device and device location use Bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth will turn on tomorrow at 5.00 a.m."</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Record issue"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Start"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Stop"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Bug report"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"What part of your device experience was affected?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Select issue type"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Screen record"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 088f751..836eefa 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -270,12 +270,15 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"See all"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Use Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connected"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Audio Sharing"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnect"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activate"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Features like Quick Share, Find My Device, and device location use Bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth will turn on tomorrow at 5 AM"</string>
+    <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Features like Quick Share and Find My Device use Bluetooth"</string>
+    <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth will turn on tomorrow morning"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio Sharing"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Sharing Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index fc27f16..d2af8a6 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"See all"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Use Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connected"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnect"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activate"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Features like Quick Share, Find My Device and device location use Bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth will turn on tomorrow at 5.00 a.m."</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Record issue"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Start"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Stop"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Bug report"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"What part of your device experience was affected?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Select issue type"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Screen record"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index fc27f16..d2af8a6 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"See all"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Use Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connected"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnect"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activate"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Features like Quick Share, Find My Device and device location use Bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth will turn on tomorrow at 5.00 a.m."</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Record issue"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Start"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Stop"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Bug report"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"What part of your device experience was affected?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Select issue type"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Screen record"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 958c645..77ef52e 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -270,12 +270,15 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‎‏‏‏‎‎‏‎‎‏‏‏‎‎‏‎‏‏‏‏‏‎‎‎‏‎‏‎‏‏‎‎‎‎‎‏‏‎‏‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‎‏‎See all‎‏‎‎‏‎"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‏‎‎‎‎‏‎‎‏‎‎‎‎‏‎‏‎‏‎‏‏‎‎‎‏‎‏‏‏‎‏‎‎‏‏‏‎‎‏‎‏‎‏‎‎‎‏‎‏‏‏‏‎Use Bluetooth‎‏‎‎‏‎"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‎‏‏‎‎‎‏‎‏‏‎‏‎‏‏‏‎‏‎‎‏‏‎‎‎‏‎‏‏‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‎‎‎‏‎‎‎Connected‎‏‎‎‏‎"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‏‏‎‎‎‏‎‎‎‎‏‎‎‎‎‏‏‎‏‏‏‏‏‏‏‎‎‏‎‏‏‎‏‎‎‏‏‏‏‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‏‎‎Audio Sharing‎‏‎‎‏‎"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‎‎‏‏‎‏‏‎‎‎‏‎‏‎‏‎‎‎‏‎‏‏‏‎‎‏‏‎‏‏‎‏‏‏‎‎‎‏‏‎‎‎‏‏‏‎‏‏‎‎‏‎‏‎Saved‎‏‎‎‏‎"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‏‏‎‎‎‏‎‏‏‏‎‏‏‎‏‏‏‏‏‎‎‏‏‏‏‏‎‎‎‏‎‏‎‎‏‎‎‏‏‏‏‎‏‎‎‏‏‎‏‎‎‏‏‏‏‎‎disconnect‎‏‎‎‏‎"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‎‏‎‏‏‏‏‎‏‏‎‎‎‎‎‏‎‏‏‎‏‎‏‏‏‎‏‏‎‏‎‏‏‏‎‏‎‏‏‎‎‎‏‏‎‏‎‎‏‎‏‏‎‏‏‎activate‎‏‎‎‏‎"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‏‏‎‎‎‎‎‏‏‏‎‎‏‎‏‏‎‏‏‏‎‎‎‏‏‎‏‏‎‏‏‏‎‏‎‏‎‏‏‏‎‎‎‎‏‎‎‏‏‎‏‏‎‎‏‎‎Automatically turn on again tomorrow‎‏‎‎‏‎"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‏‏‎‏‏‏‎‏‎‎‎‏‎‏‏‎‏‏‎‎‎‎‏‏‎‏‎‏‎‎‎‎‎‎‏‎‎‏‏‏‏‎‏‏‏‎‎‎‏‏‏‏‏‎Features like Quick Share, Find My Device, and device location use Bluetooth‎‏‎‎‏‎"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‎‏‎‎‏‎‎‎‏‏‎‎‎‏‎‏‎‎‏‏‏‏‎‎‎‎‎‏‏‏‎‏‏‏‎‏‎‏‎‏‎‎‏‎‎‏‏‎‏‎‎‏‎‏‎‎Bluetooth will turn on tomorrow at 5 AM‎‏‎‎‏‎"</string>
+    <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‏‎‏‏‏‏‎‏‎‎‏‏‏‎‎‏‎‏‎‎‏‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‎‎‎‎‏‎‎‎‎‎‎‏‎‏‎‎‎‏‏‎‎‎Features like Quick Share and Find My Device use Bluetooth‎‏‎‎‏‎"</string>
+    <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‎‎‎‎‏‏‏‎‎‎‏‎‏‏‎‎‏‎‏‏‎‎‏‏‏‏‎‏‏‎‏‏‏‏‏‎‎‏‏‎‎‎‎‎‏‎‏‎‎‏‎‏‏‏‎Bluetooth will turn on tomorrow morning‎‏‎‎‏‎"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‏‎‎‎‎‏‎‏‎‎‎‏‏‏‎‎‎‎‏‎‏‏‏‎‎‏‎‏‎‎‎‎‏‏‎‏‏‏‎‎‏‎‎‎‏‏‏‏‎‏‏‏‎‎Audio Sharing‎‏‎‎‏‎"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‎‏‏‎‎‏‏‎‎‎‏‏‎‎‏‎‎‎‏‏‎‏‎‎‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‎‎‎‏‎‎‎‏‏‏‎‎‎Sharing Audio‎‏‎‎‏‎"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‎‎‎‎‎‏‎‎‏‏‎‎‏‎‎‏‎‏‎‎‏‏‏‏‎‏‎‏‏‏‏‎‏‏‏‏‏‎‎‎‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‎‎‏‎‎‏‏‎<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>‎‏‎‎‏‏‏‎ battery‎‏‎‎‏‎"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‏‏‎‏‎‏‎‎‎‏‎‎‏‏‎‏‎‎‎‏‎‏‎‏‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‎‎‎‏‏‎‏‎‏‎‏‏‎‎‏‎Audio‎‏‎‎‏‎"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‎‏‎‏‏‏‎‏‎‏‎‎‎‎‏‎‎‏‎‎‏‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‎‏‏‏‎‎‏‏‎‏‏‎‎‏‎‏‎‎‎Headset‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index b625dc5..c33213a 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Ver todos"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Conectado"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Guardado"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activar"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Volver a activar automáticamente mañana"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Las funciones como Quick Share, Encontrar mi dispositivo y la ubicación del dispositivo usan Bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Se activará el Bluetooth mañana a las 5 a.m."</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batería"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculares"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Grabar error"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Iniciar"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Detener"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Informe de errores"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"¿Qué parte de tu exp. del disp. se vio afectada?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Seleccionar tipo de problema"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Grabadora de pant."</string>
@@ -1303,5 +1310,5 @@
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroiluminación del teclado"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivel %1$d de %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Controles de la casa"</string>
-    <string name="home_controls_dream_description" msgid="4644150952104035789">"Accede a controles de la casa como prot. de pant."</string>
+    <string name="home_controls_dream_description" msgid="4644150952104035789">"Accede rápidamente a controles de la casa como prot. de pantalla"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 583a181..1dc93fde 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Ver todos"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Conectado"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Guardado"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activar"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Volver a activar automáticamente mañana"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Las funciones como Quick Share y Encontrar mi dispositivo, y la ubicación del dispositivo usan Bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"El Bluetooth se activará mañana a las 5:00"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batería"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculares"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Problema de grabación"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Iniciar"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Detener"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Informe errores"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"¿Qué parte de tu experiencia se ha visto afectada?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecciona el tipo de problema"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Grabar pantalla"</string>
@@ -479,7 +486,7 @@
     <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Cuando compartes, grabas o envías una aplicación, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> puede acceder a todo lo que se muestre o se reproduzca en ella. Debes tener cuidado con elementos como contraseñas, detalles de pagos, mensajes, fotos, audio y vídeo."</string>
     <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Empezar"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ha inhabilitado esta opción"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"¿Empezar a enviar contenido?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"¿Empezar a enviar?"</string>
     <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Cuando envías contenido, Android puede acceder a todo lo que se muestre en la pantalla o se reproduzca en tu dispositivo. Debes tener cuidado con elementos como contraseñas, detalles de pagos, mensajes, fotos, audio y vídeo."</string>
     <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Cuando envías una aplicación, Android puede acceder a todo lo que se muestre o se reproduzca en ella. Debes tener cuidado con elementos como contraseñas, detalles de pagos, mensajes, fotos, audio y vídeo."</string>
     <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Empezar a enviar"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 190c002..fea407d 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Kuva kõik"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Kasuta Bluetoothi"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Ühendatud"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Salvestatud"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"katkesta ühendus"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiveeri"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Lülita automaatselt homme uuesti sisse"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Funktsioonid, nagu Kiirjagamine, Leia mu seade ja seadme asukoht, kasutavad Bluetoothi"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth lülitatakse sisse homme kell viis hommikul"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> akut"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Heli"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Peakomplekt"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Probleemi salvestamine"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Alusta"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Peata"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Veaaruanne"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Millist seadme kasutuskogemuse osa see mõjutas?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Valige probleemi tüüp"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekraanisalvestus"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index b60df6f..41de1d4 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Ikusi guztiak"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Erabili Bluetootha"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Konektatuta"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gordeta"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"deskonektatu"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktibatu"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Aktibatu automatikoki berriro bihar"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Quick Share, Bilatu nire gailua, gailuaren kokapena eta beste eginbide batzuek Bluetootha darabilte"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetootha bihar 05:00etan aktibatuko da"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audioa"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Entzungailua"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Arazo bat dago grabaketarekin"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Hasi"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Gelditu"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Akatsen txostena"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Gailuaren erabileraren zer alderdiri eragin dio?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Hautatu arazo mota"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Pantaila-grabaketa"</string>
@@ -735,7 +742,7 @@
     <string name="keyboard_key_numpad_template" msgid="7316338238459991821">"Zenbaki-teklatuko <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="notif_inline_reply_remove_attachment_description" msgid="7954075334095405429">"Kendu eranskina"</string>
     <string name="keyboard_shortcut_group_system" msgid="1583416273777875970">"Sistema"</string>
-    <string name="keyboard_shortcut_group_system_home" msgid="7465138628692109907">"Hasierako pantaila"</string>
+    <string name="keyboard_shortcut_group_system_home" msgid="7465138628692109907">"Orri nagusia"</string>
     <string name="keyboard_shortcut_group_system_recents" msgid="8628108256824616927">"Azkenaldikoak"</string>
     <string name="keyboard_shortcut_group_system_back" msgid="1055709713218453863">"Atzera"</string>
     <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Jakinarazpenak"</string>
@@ -1302,6 +1309,6 @@
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) aplikazioak erabili du duela gutxi"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Teklatuaren hondoko argia"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d/%2$d maila"</string>
-    <string name="home_controls_dream_label" msgid="6567105701292324257">"Etxeko gailuak kontrolatzeko aukerak"</string>
-    <string name="home_controls_dream_description" msgid="4644150952104035789">"Atzitu etxeko gailuak kontrolatzeko aukerak pantaila-babesletik"</string>
+    <string name="home_controls_dream_label" msgid="6567105701292324257">"Etxeko gailuen kontrola"</string>
+    <string name="home_controls_dream_description" msgid="4644150952104035789">"Kontrolatu etxeko gailuak pantaila-babesletik"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 0c53fa0..8e72450 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"دیدن همه"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"استفاده از بلوتوث"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"متصل"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ذخیره‌شده"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"قطع اتصال"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"فعال کردن"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"فردا دوباره به‌طور خودکار روشن شود"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"ویژگی‌هایی مثل «هم‌رسانی سریع»، «پیدا کردن دستگاهم»، و مکان دستگاه از بلوتوث استفاده می‌کنند"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"بلوتوث فردا ۵ ق.ظ روشن خواهد شد"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"شارژ باتری <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"صوت"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"هدست"</string>
@@ -1301,6 +1309,6 @@
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"اخیراً <xliff:g id="APP_NAME">%1$s</xliff:g> از آن استفاده کرده است (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"نور پس‌زمینه صفحه‌کلید"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"‏سطح %1$d از %2$d"</string>
-    <string name="home_controls_dream_label" msgid="6567105701292324257">"کنترل‌های لوازم خانگی"</string>
-    <string name="home_controls_dream_description" msgid="4644150952104035789">"دسترسی سریع به کنترل‌های لوازم خانگی به‌عنوان محافظ صفحه‌نمایش"</string>
+    <string name="home_controls_dream_label" msgid="6567105701292324257">"کنترل خانه هوشمند"</string>
+    <string name="home_controls_dream_description" msgid="4644150952104035789">"به کنترل خانه هوشمند به‌عنوان محافظ صفحه‌نمایش دسترسی سریع دارید"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 3c90847..2935d2e 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Näytä kaikki"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Käytä Bluetoothia"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Yhdistetty"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Tallennettu"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"katkaise yhteys"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivoi"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Laita automaattisesti päälle taas huomenna"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Ominaisuudet (esim. Quick Share ja Paikanna laite) ja laitteen sijainti käyttävät Bluetoothia"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth käynnistetään huomenna klo 5.00"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akun taso <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ääni"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Tallenna ongelma"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Aloita"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Lopeta"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Virheraportti"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Mitä osaa käyttökokemuksesta ongelma koski?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Valitse ongelman tyyppi"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Näytön tallentaja"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index cb56ba93..bd53d85 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Tout afficher"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Utiliser le Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connecté"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Enregistré"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"Déconnecter"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"Activer"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Activer le Bluetooth automatiquement demain"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Les fonctionnalités comme le Partage rapide, Localiser mon appareil et la position de l\'appareil utilisent le Bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Le Bluetooth s\'activera demain à 5 h"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Pile : <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Écouteurs"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Rapporter le problème"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Commencer"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Arrêter"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Rapport de bogue"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Quelle composante de l\'appareil a été affectée?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Sélectionner un type"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Enregistrement écran"</string>
@@ -1303,5 +1310,5 @@
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Rétroéclairage du clavier"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d de %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Domotique"</string>
-    <string name="home_controls_dream_description" msgid="4644150952104035789">"Accès rapide : domot. sous forme d\'Écran de veille"</string>
+    <string name="home_controls_dream_description" msgid="4644150952104035789">"Accès rapide : domotique sous forme d\'Écran de veille"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 15235de..109e767 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Tout afficher"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Utiliser le Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connecté"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Enregistré"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"dissocier"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activer"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Réactiver automatiquement demain"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Certaines fonctionnalités telles que Quick Share, Localiser mon appareil ou encore la position de l\'appareil utilisent le Bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Le Bluetooth sera activé demain à 5h00"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batterie"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Casque"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Enregistrer le problème"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Début"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Arrêter"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Rapport de bug"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Quel problème avez-vous rencontré avec votre appareil ?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Sélectionnez un type de problème"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Enregistrement de l\'écran"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 7f4ae81..06c984a 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Ver todo"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Estableceuse a conexión"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gardouse"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activar"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Volver activar automaticamente mañá"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"As funcións como Quick Share, Localizar o meu dispositivo ou a de localización do dispositivo utilizan o Bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"O Bluetooth activarase mañá ás 05:00"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batería"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculares"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Rexistrar problema"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Iniciar"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Deter"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Informe de erros"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Cal foi o problema na experiencia co dispositivo?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecciona o tipo de problema"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravación de pant."</string>
@@ -1303,5 +1310,5 @@
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroiluminación do teclado"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivel %1$d de %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Controis domóticos"</string>
-    <string name="home_controls_dream_description" msgid="4644150952104035789">"Controis domóticos como protector de pantalla"</string>
+    <string name="home_controls_dream_description" msgid="4644150952104035789">"Usa os controis domóticos como protector de pantalla"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 9425774..c9a5662 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"તમામ જુઓ"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"બ્લૂટૂથનો ઉપયોગ કરો"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"કનેક્ટેડ છે"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"સાચવેલું"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ડિસ્કનેક્ટ કરો"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"સક્રિય કરો"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"આવતીકાલે ફરીથી ઑટોમૅટિક રીતે ચાલુ કરો"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"ક્વિક શેર, Find My Device અને ડિવાઇસના લોકેશન જેવી સુવિધાઓ બ્લૂટૂથનો ઉપયોગ કરે છે"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"બ્લૂટૂથ આવતીકાલે સવારે 5 વાગ્યે ચાલુ થશે"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> બૅટરી"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ઑડિયો"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"હૅડસેટ"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index e4291ac..b2b2037 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"सभी देखें"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"ब्लूटूथ इस्तेमाल करें"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"कनेक्ट है"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"सेव किया गया"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"डिसकनेक्ट करें"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"चालू करें"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"कल फिर से अपने-आप चालू हो जाएगा"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"क्विक शेयर, Find My Device, और डिवाइस की जगह की जानकारी का पता लगाने जैसी सुविधाएं, ब्लूटूथ का इस्तेमाल करती हैं"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"कल सुबह 5 बजे ब्लूटूथ चालू हो जाएगा"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> बैटरी"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ऑडियो"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"हेडसेट"</string>
@@ -1191,7 +1199,7 @@
     <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# ऐप्लिकेशन चालू है}one{# ऐप्लिकेशन चालू है}other{# ऐप्लिकेशन चालू हैं}}"</string>
     <string name="fgs_dot_content_description" msgid="2865071539464777240">"नई जानकारी"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"ये ऐप्लिकेशन चालू हैं"</string>
-    <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"ये ऐप्लिकेशन चालू हैं और आपके इस्तेमाल न करने पर भी चल रहे हैं. इससे, ये बेहतर तरीके से फ़ंक्शन करते हैं. हालांकि, इससे बैटरी लाइफ़ पर भी असर पड़ सकता है."</string>
+    <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"ये ऐप्लिकेशन चालू हैं और आपके इस्तेमाल न करने पर भी चल रहे हैं. इससे ये बेहतर तरीके से काम कर पाते हैं. हालांकि, बैटरी लाइफ़ पर इसका असर पड़ सकता है."</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"बंद करें"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"बंद है"</string>
     <string name="clipboard_edit_text_done" msgid="4551887727694022409">"हो गया"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index e0d2478..64c35b8 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -270,19 +270,27 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Pogledajte sve"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Koristi Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Povezano"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Spremljeno"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"prekini vezu"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiviraj"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatski ponovo uključi sutra"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Značajke kao što su brzo dijeljenje, Pronađi moj uređaj i lokacija uređaja upotrebljavaju Bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth će se uključiti sutra u 5:00"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> baterije"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalice"</string>
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Unos"</string>
     <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Slušna pomagala"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Uključivanje…"</string>
-    <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Autom. zakretanje"</string>
+    <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatsko zakretanje"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatsko zakretanje zaslona"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Lokacija"</string>
     <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Čuvar zaslona"</string>
@@ -1301,6 +1309,6 @@
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Nedavno koristila aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Pozadinsko osvjetljenje tipkovnice"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Razina %1$d od %2$d"</string>
-    <string name="home_controls_dream_label" msgid="6567105701292324257">"Upr. kuć. uređ."</string>
-    <string name="home_controls_dream_description" msgid="4644150952104035789">"Brzo pristupajte Upr. kuć. uređ. kao čuvaru zasl."</string>
+    <string name="home_controls_dream_label" msgid="6567105701292324257">"Upravljanje uređajima"</string>
+    <string name="home_controls_dream_description" msgid="4644150952104035789">"Brzo upravljajte uređajima putem čuvara zaslona"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 7acfbe6..96c3e83 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Összes megtekintése"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth használata"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Csatlakozva"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Mentve"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"leválasztás"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiválás"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatikus visszakapcsolás holnap"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Az olyan funkciók, mint a Quick Share, a Készülékkereső és az eszköz helyadatai Bluetootht használnak"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"A Bluetooth bekapcsol holnap reggel 5-kor"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akkumulátor: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Hang"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Probléma rögzítése"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Indítás"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Leállítás"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Hibajelentés"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Az eszközhasználati élmény mely része érintett?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Problématípus kiválasztása"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Képernyőrögzítés"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 7dc33b6..00c3318 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Տեսնել բոլորը"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Միացնել Bluetooth-ը"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Միացված է"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Պահված է"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"անջատել"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ակտիվացնել"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Վաղը նորից ավտոմատ միացնել"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Գործառույթները, ինչպիսիք են Quick Share-ը, «Գտնել իմ սարքը» գործառույթը և սարքի տեղորոշումը, օգտագործում են Bluetooth-ը"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth-ը կմիանա վաղը՝ ժամը 05։00-ին"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Մարտկոցի լիցքը՝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Աուդիո"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ականջակալ"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Ձայնագրել"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Սկսել"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Կանգնեցնել"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Հաղորդում սխալի մասին"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Սարքի ո՞ր մասի հետ է կապված խնդիրը։"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Ընտրեք խնդրի տեսակը"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Էկրանի տեսագրում"</string>
@@ -1303,5 +1310,5 @@
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Հետին լուսավորությամբ ստեղնաշար"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d՝ %2$d-ից"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Տան կառավարման տարրեր"</string>
-    <string name="home_controls_dream_description" msgid="4644150952104035789">"Օգտագործեք տան կառավարման տարրերը որպես էկրանապահ"</string>
+    <string name="home_controls_dream_description" msgid="4644150952104035789">"Տան կառավարման տարրերը դարձրեք էկրանապահ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 3da9a90..826fc1a4 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Lihat semua"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Gunakan Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Terhubung"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Disimpan"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"putuskan koneksi"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktifkan"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Otomatis aktifkan lagi besok"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Fitur seperti Quick Share, Temukan Perangkat Saya, dan lokasi perangkat menggunakan Bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth akan diaktifkan besok pada pukul 05.00"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Baterai <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index b400f8c..1a0b3f2 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Sjá allt"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Nota Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Tengt"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Vistað"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"aftengja"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"virkja"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Kveikja sjálfkrafa aftur á morgun"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Eiginleikar á borð við flýtideilingu, „Finna tækið mitt“ og staðsetningu tækis nota Bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Kveikt verður á Bluetooth á morgun kl. 05:00"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> rafhlöðuhleðsla"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Hljóð"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Höfuðtól"</string>
@@ -1302,5 +1310,5 @@
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Baklýsing lyklaborðs"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Stig %1$d af %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Heimastýringar"</string>
-    <string name="home_controls_dream_description" msgid="4644150952104035789">"Fáðu skjótan aðgang að heimastýringum með því að stilla þær sem skjávara"</string>
+    <string name="home_controls_dream_description" msgid="4644150952104035789">"Fáðu skjótan aðgang að heimastýringum sem skjávara"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index aa7f152..d7e5cdc 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Visualizza tutti"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Usa Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Dispositivo connesso"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Dispositivo salvato"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnetti"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"attiva"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Riattiva automaticamente domani"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Funzionalità come Quick Share, Trova il mio dispositivo e la posizione del dispositivo usano il Bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Il Bluetooth verrà attivato domani alle 05:00"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Batteria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auricolare"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Registra problema"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Avvia"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Interrompi"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Segnalazione di bug"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Quali problemi ha l\'esperienza del dispositivo?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Seleziona il tipo di problema"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Regis. dello schermo"</string>
@@ -1303,5 +1310,5 @@
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroilluminazione della tastiera"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Livello %1$d di %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Controlli della casa"</string>
-    <string name="home_controls_dream_description" msgid="4644150952104035789">"Accedi ai controlli della casa dal salvaschermo"</string>
+    <string name="home_controls_dream_description" msgid="4644150952104035789">"Accedi rapidamente ai controlli della casa dal salvaschermo"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index a804271..bed2fc7 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"הצגת הכול"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"מחובר"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"נשמר"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ניתוק"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"הפעלה"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"החיבור יופעל שוב אוטומטית מחר"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"‏תכונות כמו \'שיתוף מהיר\', \'איפה המכשיר שלי\' ומיקום המכשיר משתמשות בחיבור Bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"‏‫Bluetooth יופעל מחר בשעה 5 בבוקר"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> סוללה"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"אודיו"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"אוזניות"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"תיעוד הבעיה"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"התחלה"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"עצירה"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"דיווח על באג"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"איזה חלק בחוויית השימוש שלך במכשיר הושפע?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"בחירה בסוג הבעיה"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"הקלטת המסך"</string>
@@ -479,10 +486,10 @@
     <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"‏בזמן שיתוף, הקלטה או העברה (cast) של אפליקציה, תהיה ל-<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> גישה לכל מה שגלוי באפליקציה או מופעל מהאפליקציה. מומלץ להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
     <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"התחלה"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> השביתה את האפשרות הזו"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"להתחיל את ההעברה?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"‏להפעיל Cast?"</string>
     <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"‏בזמן העברה (cast), תהיה ל-Android גישה לכל הפרטים שגלויים במסך שלך או מופעלים מהמכשיר שלך. מומלץ להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"‏בזמן העברה (cast) של אפליקציה, תהיה ל-Android גישה לכל מה שגלוי באפליקציה או מופעל מהאפליקציה. כדאי להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"‏התחלת ההעברה (cast)"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"‏בזמן Cast מאפליקציה, תהיה ל-Android גישה לכל מה שמופיע באפליקציה ולכל מדיה שפועלת בה. כדאי להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"‏הפעלת Cast"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"להתחיל את השיתוף?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"‏בזמן שיתוף, הקלטה או העברה (cast) תהיה ל-Android גישה לכל הפרטים שגלויים במסך שלך או מופעלים מהמכשיר שלך. מומלץ להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"‏בזמן שיתוף, הקלטה או העברה (cast) של אפליקציה, תהיה ל-Android גישה לכל מה שגלוי באפליקציה או מופעל מהאפליקציה. מומלץ להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
@@ -1245,7 +1252,7 @@
     <string name="home_quick_affordance_unavailable_configure_the_app" msgid="604424593994493281">"• יש לפחות מכשיר אחד או פאנל מכשיר אחד זמינים"</string>
     <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"צריך לבחור אפליקציית פתקים שתיפתח כברירת מחדל כשייעשה שימוש במקש הקיצור לכתיבת פתקים"</string>
     <string name="keyguard_affordance_enablement_dialog_notes_app_action" msgid="6821710209675089470">"בחירת אפליקציה"</string>
-    <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"מקש קיצור ללחיצה ארוכה"</string>
+    <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"צריך ללחוץ לחיצה ארוכה על הלחצן"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"ביטול"</string>
     <string name="rear_display_bottom_sheet_confirm" msgid="1507591562761552899">"כן, אני רוצה להחליף בין המסכים"</string>
     <string name="rear_display_folded_bottom_sheet_title" msgid="3930008746560711990">"פתיחת הטלפון"</string>
@@ -1302,6 +1309,6 @@
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"נעשה שימוש לאחרונה על ידי <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"התאורה האחורית במקלדת"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"‏רמה %1$d מתוך %2$d"</string>
-    <string name="home_controls_dream_label" msgid="6567105701292324257">"ממשק השליטה במכשירים"</string>
+    <string name="home_controls_dream_label" msgid="6567105701292324257">"שליטה במכשירים"</string>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"גישה מהירה לממשק השליטה במכשירים כשומר מסך"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 6cd1dcb1..f403308 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -173,7 +173,7 @@
     <string name="biometric_dialog_wrong_pin" msgid="1878539073972762803">"PIN が正しくありません"</string>
     <string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"パターンが正しくありません"</string>
     <string name="biometric_dialog_wrong_password" msgid="69477929306843790">"パスワードが正しくありません"</string>
-    <string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"間違えた回数が上限を超えました。\n<xliff:g id="NUMBER">%d</xliff:g> 秒後にもう一度お試しください。"</string>
+    <string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"試行回数が上限に達しました。\n<xliff:g id="NUMBER">%d</xliff:g> 秒後にもう一度お試しください。"</string>
     <string name="work_challenge_emergency_button_text" msgid="8946588434515599288">"緊急通報"</string>
     <string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"もう一度お試しください。入力回数: <xliff:g id="ATTEMPTS_0">%1$d</xliff:g>/<xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g> 回"</string>
     <string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"データが削除されます"</string>
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"すべて表示"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth を使用"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"接続しました"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"保存しました"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"接続を解除"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"有効化"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"明日自動的に ON に戻す"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"クイック共有、デバイスを探す、デバイスの位置情報などの機能は Bluetooth を使用します"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"午前 5 時に Bluetooth が ON になります"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"バッテリー <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"オーディオ"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ヘッドセット"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"録音に関する問題"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"開始"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"停止"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"バグレポート"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"デバイスのどの部分が影響を受けましたか?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"問題の種類を選択する"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"スクリーン レコード"</string>
@@ -549,7 +556,7 @@
     <string name="monitoring_description_parental_controls" msgid="8184693528917051626">"このデバイスは保護者によって管理されています。保護者は、あなたが使用するアプリ、あなたの現在地、デバイスの利用時間などの情報を確認したり、管理したりできます。"</string>
     <string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
     <string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"信頼エージェントがロック解除を管理"</string>
-    <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"認証の試行回数が上限を超えたため、デバイスがロックされました"</string>
+    <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"認証の試行回数が上限に達したため、デバイスがロックされました"</string>
     <string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"デバイスがロックされました\n認証に失敗しました"</string>
     <string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>。<xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
     <string name="accessibility_volume_settings" msgid="1458961116951564784">"音声の設定"</string>
@@ -606,7 +613,7 @@
     <string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"空間オーディオ"</string>
     <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"OFF"</string>
     <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"固定"</string>
-    <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ヘッド トラッキング"</string>
+    <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ヘ⁠ッ⁠ド ト⁠ラ⁠ッ⁠キ⁠ン⁠グ"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"タップすると、着信音のモードを変更できます"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ミュート"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ミュートを解除"</string>
@@ -1303,5 +1310,5 @@
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"キーボード バックライト"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"レベル %1$d/%2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ホーム コントロール"</string>
-    <string name="home_controls_dream_description" msgid="4644150952104035789">"ホーム コントロールにスクリーンセーバーとしてすばやくアクセス"</string>
+    <string name="home_controls_dream_description" msgid="4644150952104035789">"ホーム コントロールにスクリーンセーバーからすばやくアクセス"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 15bb4fc..d9483ec 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"ყველას ნახვა"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth-ის გამოყენება"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"დაკავშირებული"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"შენახული"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"კავშირის გაწყვეტა"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"გააქტიურება"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ხელახლა ავტომატურად ჩართვა ხვალ"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"ფუნქციები, როგორებიცაა „სწრაფი გაზიარება“, „ჩემი მოწყობილობის პოვნა“ და „მოწყობილობის მდებარეობა“ იყენებს Bluetooth-ს"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth ჩაირთვება ხვალ დილის 5 საათზე"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ბატარეა"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"აუდიო"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ყურსაცვამი"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"ჩაწერასთან დაკავშირებული პრობლემა"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"დაწყება"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"გაჩერება"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"სისტემის ხარვეზის ანგარიში"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"თქვენი მოწყობილობის გამოცდილების რა ნაწილზე მოხდა ზეგავლენა?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"აირჩიეთ პრობლემის ტიპი"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ეკრანის ჩანაწერი"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 835a0ca..36ae88a 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Барлығын көру"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth-ты пайдалану"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Қосылды"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Сақталды"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ажырату"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"іске қосу"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Ертең автоматты түрде қосылсын"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Quick Share, Find My Device сияқты функциялар мен құрылғы локациясы Bluetooth пайдаланады."</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth ертең таңғы сағат 5-те қосылады."</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батарея деңгейі: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Aудио"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнитура"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Ақауды жазу"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Бастау"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Тоқтату"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Қате туралы есеп"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Құрылғы қызметінің қандай түріне әсер етті?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Мәселе түрін таңдаңыз."</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Экранды жазу"</string>
@@ -604,7 +611,7 @@
     <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Дыбысын өшіру үшін түртіңіз."</string>
     <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Шуды реттеу"</string>
     <string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"Кеңістіктік дыбыс"</string>
-    <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Өшіру"</string>
+    <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Өшірілген"</string>
     <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Бекітілген"</string>
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Бас қимылын қадағалау"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Қоңырау режимін өзгерту үшін түртіңіз."</string>
@@ -1303,5 +1310,5 @@
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Пернетақта жарығы"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Деңгей: %1$d/%2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Үй басқару элементтері"</string>
-    <string name="home_controls_dream_description" msgid="4644150952104035789">"Үй басқару элементтерін скринсейвер ретінде жылдам қолдану"</string>
+    <string name="home_controls_dream_description" msgid="4644150952104035789">"Үй басқару элементтерін скринсейверден қолдану"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index d56c20d..c7868db 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"មើល​ទាំងអស់"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"ប្រើប៊្លូធូស"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"បានភ្ជាប់"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"បាន​រក្សាទុក"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ផ្ដាច់"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"បើកដំណើរការ"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"បើកដោយស្វ័យប្រវត្តិម្ដងទៀតនៅថ្ងៃស្អែក"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"មុខងារដូចជា Quick Share, រកឧបករណ៍របស់ខ្ញុំ និងប៊្លូធូសប្រើប្រាស់ទីតាំងឧបករណ៍"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"ប៊្លូធូសនឹងបើកនៅថ្ងៃស្អែកនៅម៉ោង 5 ព្រឹក"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"ថ្ម <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"សំឡេង"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"កាស"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 23bde22..42d655e 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -237,7 +237,7 @@
     <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್‍ಗಳು ಮತ್ತು ಅಧಿಸೂಚನೆಯ ಪರದೆ."</string>
     <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"ಲಾಕ್‌ ಸ್ಕ್ರೀನ್."</string>
     <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"ಕೆಲಸದ ಲಾಕ್ ಪರದೆ"</string>
-    <string name="accessibility_desc_close" msgid="8293708213442107755">"ಮುಚ್ಚು"</string>
+    <string name="accessibility_desc_close" msgid="8293708213442107755">"ಮುಚ್ಚಿ"</string>
     <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"ಸಂಪೂರ್ಣ ನಿಶ್ಯಬ್ಧ"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"ಅಲಾರಮ್‌ಗಳು ಮಾತ್ರ"</string>
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"ಅಡಚಣೆ ಮಾಡಬೇಡಿ."</string>
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"ಎಲ್ಲವನ್ನೂ ನೋಡಿ"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"ಬ್ಲೂಟೂತ್ ಬಳಸಿ"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"ಕನೆಕ್ಟ್ ಆಗಿದೆ"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ಸೇವ್ ಮಾಡಲಾಗಿದೆ"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ಡಿಸ್‌ಕನೆಕ್ಟ್ ಮಾಡಿ"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ಸಕ್ರಿಯಗೊಳಿಸಿ"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ನಾಳೆ ಪುನಃ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಆನ್ ಮಾಡಿ"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"ಕ್ವಿಕ್ ಶೇರ್, Find My Device ನಂತಹ ಫೀಚರ್‌ಗಳು ಹಾಗೂ ಸಾಧನದ ಸ್ಥಳವು ಬ್ಲೂಟೂತ್ ಅನ್ನು ಬಳಸುತ್ತವೆ"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"ಬ್ಲೂಟೂತ್ ನಾಳೆ ಬೆಳಗ್ಗೆ 5 ಗಂಟೆಗೆ ಆನ್ ಆಗುತ್ತದೆ"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ಬ್ಯಾಟರಿ"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ಆಡಿಯೋ"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ಹೆಡ್‌ಸೆಟ್"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"ರೆಕಾರ್ಡ್ ದೋಷ"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"ಪ್ರಾರಂಭಿಸಿ"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"ನಿಲ್ಲಿಸಿ"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"ಬಗ್ ವರದಿ ಮಾಡುವಿಕೆ"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ಸಾಧನ ಬಳಸುವಾಗ ನೀವು ಯಾವ ರೀತಿಯ ಸಮಸ್ಯೆ ಎದುರಿಸುತ್ತೀರಿ?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ಸಮಸ್ಯೆಯ ಪ್ರಕಾರವನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡ್"</string>
@@ -367,7 +374,7 @@
     <string name="quick_settings_contrast_high" msgid="656049259587494499">"ಹೆಚ್ಚು"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ಹಿಯರಿಂಗ್ ಸಾಧನಗಳು"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ಹಿಯರಿಂಗ್ ಸಾಧನಗಳು"</string>
-    <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ಹೊಸ ಸಾಧನವನ್ನು ಜೋಡಿಸಿ"</string>
+    <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ಹೊಸ ಸಾಧನವನ್ನು ಪೇರ್ ಮಾಡಿ"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"ಹೊಸ ಸಾಧನವನ್ನು ಜೋಡಿಸಲು ಕ್ಲಿಕ್ ಮಾಡಿ"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ಸಾಧನದ ಮೈಕ್ರೋಫೋನ್ ನಿರ್ಬಂಧವನ್ನು ತೆಗೆಯಬೇಕೆ?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ಸಾಧನದ ಕ್ಯಾಮರಾ ನಿರ್ಬಂಧವನ್ನು ತೆಗೆಯಬೇಕೆ?"</string>
@@ -1302,6 +1309,6 @@
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"ಇತ್ತೀಚೆಗೆ <xliff:g id="APP_NAME">%1$s</xliff:g> ಇದನ್ನು ಬಳಸಿದೆ (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ಕೀಬೋರ್ಡ್ ಬ್ಯಾಕ್‌ಲೈಟ್"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d ರಲ್ಲಿ %1$d ಮಟ್ಟ"</string>
-    <string name="home_controls_dream_label" msgid="6567105701292324257">"ಹೋಮ್ ನಿಯಂತ್ರಣಗಳು"</string>
-    <string name="home_controls_dream_description" msgid="4644150952104035789">"ಹೋಮ್ ನಿಯಂತ್ರಣವನ್ನು ಸ್ಕ್ರೀನ್‌ಸೇವರ್‌ನಂತೆ ತ್ವರಿತವಾಗಿ ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಿ"</string>
+    <string name="home_controls_dream_label" msgid="6567105701292324257">"ಮನೆ ನಿಯಂತ್ರಣಗಳು"</string>
+    <string name="home_controls_dream_description" msgid="4644150952104035789">"ಮನೆ ನಿಯಂತ್ರಣವನ್ನು ಸ್ಕ್ರೀನ್‌ಸೇವರ್‌ನಂತೆ ಬೇಗ ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಿ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 8f82465..3c34f8d 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"모두 보기"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"블루투스 사용"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"연결됨"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"저장됨"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"연결 해제"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"실행"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"내일 다시 자동으로 사용 설정"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Quick Share, 내 기기 찾기, 기기 위치 등의 기능에서 블루투스를 사용합니다."</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"블루투스가 내일 오전 5시에 켜집니다."</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"배터리 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"오디오"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"헤드셋"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"문제 기록"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"시작"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"중지"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"버그 신고"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"기기 경험의 어떤 부분에 영향이 있었나요?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"문제 유형 선택"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"화면 녹화"</string>
@@ -1303,5 +1310,5 @@
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"키보드 백라이트"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d단계 중 %1$d단계"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"홈 컨트롤"</string>
-    <string name="home_controls_dream_description" msgid="4644150952104035789">"화면 보호기로 홈 컨트롤에 빠르게 액세스합니다."</string>
+    <string name="home_controls_dream_description" msgid="4644150952104035789">"화면 보호기로 홈 컨트롤에 빠르게 액세스하기"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 40a427c..ad9d2f7 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Баарын көрүү"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Иштетүү"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Туташты"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Сакталды"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ажыратуу"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"иштетүү"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Эртең автоматтык түрдө кайра күйгүзүү"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Тез Бөлүшүү, \"Түзмөгүм кайда?\" жана түзмөктүн турган жерин аныктоо сыяктуу функциялар Bluetooth\'ду колдонот"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth эртең саат 05:00 күйөт"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батареянын деңгээли <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнитура"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index f7dfc7e..eb9e1eb 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"ເບິ່ງທັງໝົດ"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"ໃຊ້ Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"ເຊື່ອມຕໍ່ແລ້ວ"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ບັນທຶກແລ້ວ"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ຕັດການເຊື່ອມຕໍ່"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ເປີດນຳໃຊ້"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ເປີດໃຊ້ໂດຍອັດຕະໂນມັດອີກຄັ້ງມື້ອື່ນ"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"ຄຸນສົມບັດຕ່າງໆ ເຊັ່ນ: ການແຊຣ໌ດ່ວນ, ຊອກຫາອຸປະກອນຂອງຂ້ອຍ ແລະ ສະຖານທີ່ຂອງອຸປະກອນແມ່ນໃຊ້ Bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth ຈະເປີດມື້ອື່ນເວລາ 05:00 ໂມງ"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"ແບັດເຕີຣີ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ສຽງ"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ຊຸດຫູຟັງ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 734daf8..63971ab 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Žiūrėti viską"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"„Bluetooth“ naudojimas"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Prisijungta"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Išsaugota"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"atjungti"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"suaktyvinti"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatiškai vėl įjungti rytoj"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Tokioms funkcijoms kaip „Spartusis bendrinimas“, „Rasti įrenginį“ ir įrenginio vietovė naudojamas „Bluetooth“ ryšys"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"„Bluetooth“ bus įjungtas rytoj, 5 val."</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akumuliatorius: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Garsas"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Virtualiosios realybės įrenginys"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Įrašyti problemą"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Pradėti"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Stabdyti"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Pranešimas apie riktą"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Kuri įrenginio funkcija buvo paveikta?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Pasirinkite problemos tipą"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekrano įrašas"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 74a9239..4b7507d 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Skatīt visas"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Izmantot Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Savienojums izveidots"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saglabāta"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"atvienot"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivizēt"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automātiski atkal ieslēgt rīt"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Tādas funkcijas kā “Ātrā kopīgošana”, “Atrast ierīci” un ierīces atrašanās vietas noteikšana izmanto tehnoloģiju Bluetooth."</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth tiks ieslēgts rīt plkst. 5:00"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akumulators: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Austiņas"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Problēmas ierakstīšana"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Sākt"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Apturēt"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Kļūdas pārskats"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Kuras ierīces funkcijas tika ietekmētas?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Atlasiet problēmas veidu"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekrāna ierakstīšana"</string>
@@ -604,7 +611,7 @@
     <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Pieskarieties, lai izslēgtu skaņu."</string>
     <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Trokšņu kontrole"</string>
     <string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"Telpiskais audio"</string>
-    <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Izslēgts"</string>
+    <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Izslēgta"</string>
     <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fiksēts"</string>
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Seko galvai"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Pieskarieties, lai mainītu zvanītāja režīmu."</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 36d49e4..ba29ee6 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Прикажи ги сите"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Користи Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Поврзано"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Зачувано"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"прекини врска"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"активирај"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Автоматски вклучи повторно утре"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Функциите како „Брзо споделување“, „Најди го мојот уред“ и локација на уредот користат Bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth ќе се вклучи утре во 5:00"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батерија: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Слушалки"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Евидентирајте проблем"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Започнете"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Сопрете"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Извештај за грешка"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Кој дел од доживувањето на уредот беше засегнат?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Изберете тип проблем"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Снимање екран"</string>
@@ -1303,5 +1310,5 @@
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Осветлување на тастатура"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Ниво %1$d од %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Контроли за домот"</string>
-    <string name="home_controls_dream_description" msgid="4644150952104035789">"Брзо прист. до контр. за дом. како штедач на екран"</string>
+    <string name="home_controls_dream_description" msgid="4644150952104035789">"Контролите за домот како штедач на екран"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index b20faa9..8e9392c 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"എല്ലാം കാണുക"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth ഉപയോഗിക്കുക"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"കണക്‌റ്റ് ചെയ്‌തു"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"സംരക്ഷിച്ചു"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"വിച്ഛേദിക്കുക"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"സജീവമാക്കുക"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"നാളെ വീണ്ടും സ്വയമേവ ഓണാക്കുക"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"ക്വിക്ക് ഷെയർ, Find My Device, ഉപകരണ ലൊക്കേഷൻ എന്നിവ പോലുള്ള ഫീച്ചറുകൾ Bluetooth ഉപയോഗിക്കുന്നു"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth നാളെ 5 AM-ന് ഓണാക്കും"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ബാറ്ററി"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ഓഡിയോ"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ഹെഡ്‌സെറ്റ്"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 333ab55..aab22b3 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Бүгдийг харах"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth-г ашиглах"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Холбогдсон"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Хадгалсан"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"салгах"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"идэвхжүүлэх"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Маргааш автоматаар дахин асаах"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Түргэн хуваалцах, Миний төхөөрөмжийг олох зэрэг онцлогууд болон төхөөрөмжийн байршил Bluetooth-г ашигладаг"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth маргааш ҮӨ 5 цагт асна"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> батарей"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Чихэвч"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Асуудлыг бичих"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Эхлүүлэх"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Зогсоох"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Алдааны мэдээ"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Таны төхөөрөмжийн хэрэглээний аль хэсэгт нөлөөлсөн бэ?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Асуудлын төрөл сонгоно уу"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Дэлгэцийн бичлэг"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 5bed1b4..071935a 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"सर्व पहा"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"ब्‍लूटूथ वापरा"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"कनेक्ट केले"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"सेव्ह केले"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"डिस्कनेक्ट करा"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ॲक्टिव्हेट करा"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"उद्या पुन्हा आपोआप सुरू करा"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"क्विक शेअर, Find My Device आणि डिव्हाइसचे स्थान यांसारखी वैशिष्ट्ये ब्लूटूथ वापरतात"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"ब्लूटूथ उद्या सकाळी ५ वाजता सुरू होईल"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> बॅटरी"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ऑडिओ"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"हेडसेट"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"समस्या रेकॉर्ड करा"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"सुरुवात करा"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"थांबवा"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"बग रिपोर्ट"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"तुमच्या डिव्हाइसबाबत कोणत्या अनुभवावर परिणाम झाला?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"समस्येचा प्रकार निवडा"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"स्क्रीन रेकॉर्ड"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 4df6540..95d4237 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Lihat semua"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Gunakan Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Disambungkan"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Disimpan"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"putuskan sambungan"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktifkan"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Dihidupkan sekali lagi esok secara automatik"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Ciri seperti Quick Share, Find My Device dan lokasi peranti menggunakan Bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth akan dihidupkan esok pada pukul 5 PG"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> bateri"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Set Kepala"</string>
@@ -1302,5 +1310,5 @@
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Cahaya latar papan kekunci"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Tahap %1$d daripada %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Kawalan Rumah"</string>
-    <string name="home_controls_dream_description" msgid="4644150952104035789">"Akses kawalan rumah anda sebagai penyelamat skrin dengan cepat"</string>
+    <string name="home_controls_dream_description" msgid="4644150952104035789">"Jadikan kawalan rumah anda sebagai penyelamat skrin"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index a1894b0..7941935 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"အားလုံးကြည့်ရန်"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"ဘလူးတုသ်သုံးရန်"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"ချိတ်ဆက်ထားသည်"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"သိမ်းထားသည်"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ချိတ်ဆက်မှုဖြုတ်ရန်"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"စသုံးရန်"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"မနက်ဖြန် အလိုအလျောက် ထပ်ဖွင့်ရန်"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"‘အမြန် မျှဝေပါ’၊ Find My Device နှင့် စက်ပစ္စည်းတည်နေရာကဲ့သို့ တူးလ်များသည် ဘလူးတုသ်သုံးသည်"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"မနက်ဖြန် မနက် ၅ နာရီတွင် ဘလူးတုသ်ကို ဖွင့်ပါမည်"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ဘက်ထရီ"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"အသံ"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"မိုက်ခွက်ပါနားကြပ်"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"ပြဿနာကို မှတ်တမ်းတင်ခြင်း"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"စတင်ပါ"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"ရပ်ပါ"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"ချွတ်ယွင်းမှု အစီရင်ခံစာ"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"စက်အသုံးပြုမှု၏ မည်သည့်အပိုင်းကို သက်ရောက်သလဲ။"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ပြဿနာအမျိုးအစား ရွေးရန်"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ဖန်သားပြင်ရိုက်ကူးရန်"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 5039150d..59afb3e 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Se alle"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bruk Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Tilkoblet"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Lagret"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"koble fra"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiver"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Slå på igjen i morgen automatisk"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Funksjoner som Quick Share, Finn enheten min og enhetsposisjon bruker Bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth slås på i morgen kl. 05.00"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batteri"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Lyd"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Hodetelefoner"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Registrer problem"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Start"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Stopp"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Feilrapport"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Hvilken del av enhetsopplevelsen din ble påvirket?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Velg problemtype"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skjermopptak"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 28c7b05..3ab647f 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"सबै डिभाइसहरू हेर्नुहोस्"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"ब्लुटुथ प्रयोग गर्नुहोस्"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"कनेक्ट गरिएको छ"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"सेभ गरिएको छ"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"डिस्कनेक्ट गर्नुहोस्"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"एक्टिभेट गर्नुहोस्"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"भोलि फेरि स्वतः अन गर्नुहोस्"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"क्विक सेयर, Find My Device र डिभाइसको लोकेसन जस्ता सुविधाहरूले ब्लुटुथ प्रयोग गर्छन्"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"ब्लुटुथ भोलि बिहान ५ बजे अन हुने छ"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ब्याट्री"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"अडियो"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"हेडसेट"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index d0500c0..e85a1da 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Alles tonen"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth gebruiken"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Verbonden"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Opgeslagen"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"loskoppelen"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activeren"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Morgen weer automatisch aanzetten"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Functies zoals Quick Share, Vind mijn apparaat en apparaatlocatie maken gebruik van bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth gaat morgen om 05:00 uur aan"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batterijniveau"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Probleem vastleggen"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Starten"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Stoppen"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Bugrapport"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Op welk onderdeel van de apparaatfunctionaliteit had dit effect?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Probleemtype selecteren"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Schermopname"</string>
@@ -606,7 +613,7 @@
     <string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"Ruimtelijke audio"</string>
     <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Uit"</string>
     <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Vast"</string>
-    <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Hoofdbeweging volgen"</string>
+    <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Hoofd­beweging volgen"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Tik om de beltoonmodus te wijzigen"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"geluid uit"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"geluid aanzetten"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index eb6865e..77ed062 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"ସବୁ ଦେଖନ୍ତୁ"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"ବ୍ଲୁଟୁଥ ବ୍ୟବହାର କରନ୍ତୁ"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"କନେକ୍ଟ କରାଯାଇଛି"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ସେଭ କରାଯାଇଛି"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ଡିସକନେକ୍ଟ କରନ୍ତୁ"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ଚାଲୁ କରନ୍ତୁ"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ଆସନ୍ତାକାଲି ସ୍ୱତଃ ପୁଣି ଚାଲୁ ହେବ"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Quick Share, Find My Device ଏବଂ ଡିଭାଇସ ଲୋକେସନ ପରି ଫିଚରଗୁଡ଼ିକ ବ୍ଲୁଟୁଥ ବ୍ୟବହାର କରେ"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"ବ୍ଲୁଟୁଥ ଆସନ୍ତାକାଲି 5 AMରେ ଚାଲୁ ହେବ"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ବ୍ୟାଟେରୀ"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ଅଡିଓ"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ହେଡସେଟ୍‍"</string>
@@ -1302,5 +1310,5 @@
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"କୀବୋର୍ଡ ବେକଲାଇଟ"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dରୁ %1$d ନମ୍ବର ଲେଭେଲ"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ହୋମ କଣ୍ଟ୍ରୋଲ୍ସ"</string>
-    <string name="home_controls_dream_description" msgid="4644150952104035789">"ସ୍କ୍ରିନସେଭର ଭାବେ ହୋମ କଣ୍ଟ୍ରୋଲ୍ସକୁ ଶୀଘ୍ର ଆକ୍ସେସ କର"</string>
+    <string name="home_controls_dream_description" msgid="4644150952104035789">"ସ୍କ୍ରିନସେଭର ଭାବେ ହୋମ କଣ୍ଟ୍ରୋଲ୍ସକୁ ଶୀଘ୍ର ଆକ୍ସେସ କରନ୍ତୁ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 3066c1d..0cc753b 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"ਸਭ ਦੇਖੋ"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"ਬਲੂਟੁੱਥ ਵਰਤੋ"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"ਕਨੈਕਟ ਹੈ"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ਰੱਖਿਅਤ ਕੀਤਾ ਗਿਆ"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ਡਿਸਕਨੈਕਟ ਕਰੋ"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ਕਿਰਿਆਸ਼ੀਲ ਕਰੋ"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ਕੱਲ੍ਹ ਨੂੰ ਆਪਣੇ ਆਪ ਚਾਲੂ ਹੋ ਜਾਵੇਗਾ"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"ਕਵਿੱਕ ਸ਼ੇਅਰ, Find My Device ਅਤੇ ਡੀਵਾਈਸ ਦਾ ਟਿਕਾਣਾ ਵਰਗੀਆਂ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਬਲੂਟੁੱਥ ਦੀ ਵਰਤੋਂ ਕਰਦੀਆਂ ਹਨ"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"ਬਲੂਟੁੱਥ ਕੱਲ੍ਹ ਸਵੇਰੇ 5 ਵਜੇ ਚਾਲੂ ਹੋਵੇਗਾ"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ਬੈਟਰੀ"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ਆਡੀਓ"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ਹੈੱਡਸੈੱਟ"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"ਸਮੱਸਿਆ ਰਿਕਾਰਡ ਕਰੋ"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"ਸ਼ੁਰੂ ਕਰੋ"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"ਬੰਦ ਕਰੋ"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"ਬੱਗ ਰਿਪੋਰਟ"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਦੀ ਕਿਹੜੀ ਸੁਵਿਧਾ ਪ੍ਰਭਾਵਿਤ ਹੋਈ ਸੀ?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ਸਮੱਸਿਆ ਦੀ ਕਿਸਮ ਚੁਣੋ"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 6ff18b9..814d321 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -270,12 +270,15 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Pokaż wszystkie"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Używaj Bluetootha"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Połączone"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Udostępnianie dźwięku"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Zapisane"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"rozłącz"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktywuj"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatycznie włącz ponownie jutro"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Funkcje takie jak szybkie udostępnianie, Znajdź moje urządzenie czy lokalizacja urządzenia używają Bluetootha"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth włączy się jutro o 5 rano"</string>
+    <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funkcje takie jak szybkie udostępnianie czy Znajdź moje urządzenie korzystają z Bluetootha"</string>
+    <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth włączy się jutro rano"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Udostępnianie dźwięku"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Udostępniam dźwięk"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> naładowania baterii"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Dźwięk"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Zestaw słuchawkowy"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 4d8156b..17fde25 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Mostrar tudo"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Conectado"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Salvo"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ativar"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Ativar automaticamente de novo amanhã"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Recursos como o Quick Share, o Encontre Meu Dispositivo e a localização do dispositivo usam o Bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"O Bluetooth será ativado amanhã às 5h"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Áudio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Fone de ouvido"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Problema na gravação"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Iniciar"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Parar"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Relatório do bug"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Que parte da sua experiência no dispositivo foi afetada?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecionar tipo de problema"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravação de tela"</string>
@@ -1303,5 +1310,5 @@
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Luz de fundo do teclado"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nível %1$d de %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Automação residencial"</string>
-    <string name="home_controls_dream_description" msgid="4644150952104035789">"Acesse rapidamente a automação residencial como um protetor de tela"</string>
+    <string name="home_controls_dream_description" msgid="4644150952104035789">"Controles de automação residencial no protetor de tela"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 3c7578f..dfb7695 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Ver tudo"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Ligado"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Guardado"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desassociar"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ativar"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Reativar amanhã automaticamente"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"As funcionalidades como Partilha rápida, Localizar o meu dispositivo e localização do dispositivo usam o Bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"O Bluetooth vai ser ativado amanhã às 05:00"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de bateria"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Áudio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ausc. c/ mic. integ."</string>
@@ -1302,5 +1310,5 @@
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Luz do teclado"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nível %1$d de %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Controlos domésticos"</string>
-    <string name="home_controls_dream_description" msgid="4644150952104035789">"Aceda rapid. aos contr. domést. como prot. de ecrã"</string>
+    <string name="home_controls_dream_description" msgid="4644150952104035789">"Use controlos domésticos como proteção de ecrã"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 4d8156b..17fde25 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Mostrar tudo"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Conectado"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Salvo"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ativar"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Ativar automaticamente de novo amanhã"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Recursos como o Quick Share, o Encontre Meu Dispositivo e a localização do dispositivo usam o Bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"O Bluetooth será ativado amanhã às 5h"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Áudio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Fone de ouvido"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Problema na gravação"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Iniciar"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Parar"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Relatório do bug"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Que parte da sua experiência no dispositivo foi afetada?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecionar tipo de problema"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravação de tela"</string>
@@ -1303,5 +1310,5 @@
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Luz de fundo do teclado"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nível %1$d de %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Automação residencial"</string>
-    <string name="home_controls_dream_description" msgid="4644150952104035789">"Acesse rapidamente a automação residencial como um protetor de tela"</string>
+    <string name="home_controls_dream_description" msgid="4644150952104035789">"Controles de automação residencial no protetor de tela"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 172ee12..2af8013 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Afișează tot"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Folosește Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Conectat"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Salvat"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"deconectează"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activează"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Activează din nou automat mâine"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Funcții precum Quick Share, Găsește-mi dispozitivul și locația dispozitivului folosesc Bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth se va activa mâine la 5 dimineața"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Nivelul bateriei: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Căști"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Problemă legată de înregistrare"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Începe"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Oprește"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Raport de eroare"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Ce parte a experienței pe dispozitiv a fost afectată?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selectează tipul problemei"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Înregistrarea ecranului"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 249be7fe..16fe331 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Все"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Использовать"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Подключено"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Сохранено"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"отключить"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"активировать"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Включить завтра автоматически"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Bluetooth используется в сервисе \"Найти устройство\", таких функциях, как Быстрая отправка, и при определении местоположения устройства"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth будет включен завтра в 05:00"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Заряд: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудиоустройство"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнитура"</string>
@@ -1302,5 +1310,5 @@
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Подсветка клавиатуры"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Уровень %1$d из %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Управление домом"</string>
-    <string name="home_controls_dream_description" msgid="4644150952104035789">"Добавьте настройки умного дома на заставку"</string>
+    <string name="home_controls_dream_description" msgid="4644150952104035789">"Быстрый доступ к управлению домом через заставку"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 90c87f0..458d9ee 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"සියල්ල බලන්න"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"බ්ලූටූත් භාවිතා කරන්න"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"සම්බන්ධිතයි"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"සුරැකිණි"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"විසන්ධි කරන්න"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"සක්‍රිය කරන්න"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"හෙට ස්වයංක්‍රීයව නැවත ක්‍රියාත්මක කරන්න"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"ඉක්මන් බෙදා ගැනීම, මගේ උපාංගය සෙවීම, සහ උපාංග ස්ථානය වැනි විශේෂාංග බ්ලූටූත් භාවිත කරයි"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"බ්ලූටූත් හෙට පෙ.ව. 5ට ක්‍රියාත්මක වනු ඇත"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"බැටරිය <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ශ්‍රව්‍ය"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"හෙඩ්සෙටය"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"පටිගත කිරීමේ ගැටලුව"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"අරඹන්න"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"නවත්වන්න"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"දෝෂ වර්තාව"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ඔබේ උපාංග අත්දැකීමේ කුමන කොටසට බලපෑවේ ද?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ගැටලු වර්ගය තෝරන්න"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"තිර පටිගත කිරීම"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 7751c6d..c8c2ee5 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Zobraziť všetko"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Použiť Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Pripojené"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Uložené"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"odpojiť"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivovať"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automaticky zajtra znova zapnúť"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Funkcie, ako sú Quick Share, Nájdi moje zariadenie a poloha zariadenia, používajú Bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth sa zapne zajtra o 5:00."</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Batéria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvuk"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Náhlavná súprava"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 53be1bd..0302199 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Pokaži vse"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Uporabi Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Povezano"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Shranjeno"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"prekinitev povezave"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiviranje"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Samodejno znova vklopi jutri"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Funkcije, kot so Hitro deljenje, Poišči mojo napravo in zaznavanje lokacije naprave, uporabljajo Bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth se bo vklopil jutri ob 5. uri"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Baterija na <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvok"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalke z mikrofonom"</string>
@@ -1302,5 +1310,5 @@
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Osvetlitev tipkovnice"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Stopnja %1$d od %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrolniki za dom"</string>
-    <string name="home_controls_dream_description" msgid="4644150952104035789">"Hiter dostop do kontrol. za dom na ohranj. zaslona"</string>
+    <string name="home_controls_dream_description" msgid="4644150952104035789">"Hiter dostop do kontrolnikov za dom na ohranjevalniku zaslona"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 7e75984..8e621ce 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Shiko të gjitha"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Përdor Bluetooth-in"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Lidhur"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Ruajtur"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"shkëput"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivizo"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Aktivizoje automatikisht nesër"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Veçoritë si \"Ndarja e shpejtë\", \"Gjej pajisjen time\" dhe vendndodhja e pajisjes përdorin Bluetooth-in"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth-i do të aktivizohet nesër në 5:00"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> bateri"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Kufje me mikrofon"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Regjistro problemin"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Nis"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Ndalo"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Raporti i defekteve në kod"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Cila pjesë e përvojës me pajisjen është prekur?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Zgjidh llojin e problemit"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Regjistrim i ekranit"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 2b1882d..a1825bf 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Прикажи све"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Користи Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Повезано"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Сачувано"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"прекините везу"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"активирајте"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Аутоматски поново укључи сутра"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Функције као што су Quick Share, Пронађи мој уређај и локација уређаја користе Bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth ће се укључити сутра у 5:00"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Ниво батерије је <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Слушалице"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index f075c8c..abb5d09 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Se alla"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Använd Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Ansluten"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Sparad"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"koppla från"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivera"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Aktivera automatiskt igen i morgon"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Funktioner som Snabbdelning, Hitta min enhet och enhetens plats använder Bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth aktiveras i morgon kl. 5.00"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batteri"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ljud"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Registrera problem"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Starta"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Stoppa"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Felrapport"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Vilken enhetsupplevelse påverkades?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Välj problemtyp"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skärminspelning"</string>
@@ -1303,5 +1310,5 @@
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Bakgrundsbelysning för tangentbord"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivå %1$d av %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Hemstyrning"</string>
-    <string name="home_controls_dream_description" msgid="4644150952104035789">"Kom snabbt åt hemstyrningen som en skärmsläckare"</string>
+    <string name="home_controls_dream_description" msgid="4644150952104035789">"Kom snabbt åt hemstyrningen via skärmsläckaren"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 102926b..392a74d 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Angalia vyote"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Tumia Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Imeunganishwa"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Imehifadhiwa"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ondoa"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"anza kutumia"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Iwashe tena kesho kiotomatiki"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Vipengele kama vile Kutuma Haraka, Tafuta Kifaa Changu na mahali kifaa kilipo hutumia Bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth itawaka kesho saa 11 alfajiri"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Chaji ya betri ni <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Sauti"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Vifaa vya sauti"</string>
@@ -1301,6 +1309,6 @@
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Ilitumiwa hivi majuzi na <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Mwanga chini ya kibodi"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Kiwango cha %1$d kati ya %2$d"</string>
-    <string name="home_controls_dream_label" msgid="6567105701292324257">"Vidhibiti vya Vifaa Nyumbani"</string>
-    <string name="home_controls_dream_description" msgid="4644150952104035789">"Fikia haraka vidhibiti vya vifaa nyumbani kama taswira ya skrini"</string>
+    <string name="home_controls_dream_label" msgid="6567105701292324257">"Dhibiti Vifaa Nyumbani"</string>
+    <string name="home_controls_dream_description" msgid="4644150952104035789">"Fikia haraka vidhibiti vya vifaa nyumbani vikiwa taswira ya skrini"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 9afa0d1..dfc00df 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"அனைத்தையும் காட்டு"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"புளூடூத்தைப் பயன்படுத்துதல்"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"இணைக்கப்பட்டது"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"சேமிக்கப்பட்டது"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"இணைப்பு நீக்கும்"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"செயல்படுத்தும்"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"நாளைக்குத் தானாகவே மீண்டும் இயக்கப்படும்"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"விரைவுப் பகிர்தல், Find My Device போன்ற அம்சங்களும் சாதன இருப்பிடமும் புளூடூத்தைப் பயன்படுத்துகின்றன"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"நாளை 5 AMக்கு புளூடூத் ஆன் ஆகும்"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> பேட்டரி"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ஆடியோ"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ஹெட்செட்"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"சிக்கலை ரெக்கார்டு செய்தல்"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"தொடங்குங்கள்"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"நிறுத்துங்கள்"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"பிழை அறிக்கை"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"சாதன அனுபவத்தின் எந்தப் பகுதி பாதிக்கப்பட்டது?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"சிக்கல் வகையைத் தேர்வுசெய்க"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ஸ்கிரீன் ரெக்கார்டு"</string>
@@ -1303,5 +1310,5 @@
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"கீபோர்டு பேக்லைட்"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"நிலை, %2$d இல் %1$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ஹோம் கன்ட்ரோல்கள்"</string>
-    <string name="home_controls_dream_description" msgid="4644150952104035789">"ஹோம் கன்ட்ரோல்களை ஸ்கிரீன் சேவராக விரைவாக அணுகலாம்"</string>
+    <string name="home_controls_dream_description" msgid="4644150952104035789">"ஹோம் கன்ட்ரோல்களை ஸ்கிரீன் சேவராக அணுகலாம்"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 7f82ebf..518883f 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"అన్నింటినీ చూడండి"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"బ్లూటూత్ వాడండి"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"కనెక్ట్ అయింది"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"సేవ్ చేయబడింది"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"డిస్‌కనెక్ట్ చేయండి"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"యాక్టివేట్ చేయండి"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"రేపు మళ్లీ ఆటోమేటిక్‌గా ఆన్ చేస్తుంది"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"క్విక్ షేర్, Find My Device, పరికర లొకేషన్ వంటి ఫీచర్‌లు బ్లూటూత్‌ను ఉపయోగిస్తాయి"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"బ్లూటూత్ రేపు ఉదయం 5 గంటలకు ఆన్ అవుతుంది"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> బ్యాటరీ"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ఆడియో"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"హెడ్‌సెట్"</string>
@@ -1302,5 +1310,5 @@
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"కీబోర్డ్ బ్యాక్‌లైట్"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dలో %1$dవ స్థాయి"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"హోమ్ కంట్రోల్స్"</string>
-    <string name="home_controls_dream_description" msgid="4644150952104035789">"స్క్రీన్ సేవర్‌గా మీ హోమ్ కంట్రోల్స్‌ను త్వరగా యాక్సెస్ చేయండి"</string>
+    <string name="home_controls_dream_description" msgid="4644150952104035789">"హోమ్ కంట్రోల్స్‌ను స్క్రీన్ సేవర్‌గా చేసి వేగంగా యాక్సెస్ పొందండి"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index dece9c1..77f506c 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -260,7 +260,7 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"ขณะนี้หน้าจอถูกล็อกให้วางในแนวนอน"</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"ขณะนี้หน้าจอถูกล็อกให้วางในแนวตั้ง"</string>
     <string name="dessert_case" msgid="9104973640704357717">"ชั้นแสดงของหวาน"</string>
-    <string name="start_dreams" msgid="9131802557946276718">"โปรแกรมรักษาหน้าจอ"</string>
+    <string name="start_dreams" msgid="9131802557946276718">"ภาพพักหน้าจอ"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"อีเทอร์เน็ต"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"ห้ามรบกวน"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"บลูทูธ"</string>
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"ดูทั้งหมด"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"ใช้บลูทูธ"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"เชื่อมต่อแล้ว"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"บันทึกแล้ว"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ยกเลิกการเชื่อมต่อ"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"เปิดใช้งาน"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"เปิดอีกครั้งโดยอัตโนมัติในวันพรุ่งนี้"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"ฟีเจอร์ต่างๆ เช่น Quick Share, หาอุปกรณ์ของฉัน และตำแหน่งของอุปกรณ์ ใช้บลูทูธ"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"บลูทูธจะเปิดพรุ่งนี้เวลา 05:00 น."</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"แบตเตอรี่ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"เสียง"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ชุดหูฟัง"</string>
@@ -747,7 +755,7 @@
     <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"ไม่พบแป้นพิมพ์ลัด"</string>
     <string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"ระบบ"</string>
     <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"อินพุต"</string>
-    <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"แอปที่เปิดอยู่"</string>
+    <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"เปิดแอป"</string>
     <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"แอปปัจจุบัน"</string>
     <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"แสดงผลการค้นหา"</string>
     <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"แสดงแป้นพิมพ์ลัดสำหรับระบบ"</string>
@@ -1302,5 +1310,5 @@
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ไฟแบ็กไลต์ของแป้นพิมพ์"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"ระดับที่ %1$d จาก %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ระบบควบคุมอุปกรณ์สมาร์ทโฮม"</string>
-    <string name="home_controls_dream_description" msgid="4644150952104035789">"เข้าถึงระบบควบคุมอุปกรณ์สมาร์ทโฮมได้อย่างรวดเร็วที่การพักหน้าจอ"</string>
+    <string name="home_controls_dream_description" msgid="4644150952104035789">"เข้าถึงระบบควบคุมอุปกรณ์สมาร์ทโฮมได้อย่างรวดเร็วผ่านภาพพักหน้าจอ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 9f408f4..922bf88 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Tingnan lahat"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Gumamit ng Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Nakakonekta"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Na-save"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"idiskonekta"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"i-activate"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Awtomatikong i-on ulit bukas"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Guamgamit ng Bluetooth ang mga feature tulad ng Quick Share, Hanapin ang Aking Device, at lokasyon ng device"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Mag-o-on ang Bluetooth bukas nang 5 AM"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> na baterya"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index c07c350..35f13de 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Tümünü göster"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth\'u kullan"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Bağlandı"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Kaydedildi"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"bağlantıyı kes"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"etkinleştir"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Yarın otomatik olarak tekrar aç"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Quick Share, Cihazımı Bul ve cihaz konumu gibi özellikler Bluetooth\'u kullanır"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth yarın saat 05:00\'te açılacak"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Pil düzeyi <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ses"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Mikrofonlu kulaklık"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Sorunu Kaydedin"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Başlayın"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Durdurun"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Hata Raporu"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Cihaz deneyiminiz ne şekilde etkilendi?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Sorun türünü seçin"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekran kaydedicisi"</string>
@@ -1303,5 +1310,5 @@
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klavye aydınlatması"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Seviye %1$d / %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Ev Kontrolleri"</string>
-    <string name="home_controls_dream_description" msgid="4644150952104035789">"Ekran koruyucu olan ev kontrollerinize hızlıca erişin"</string>
+    <string name="home_controls_dream_description" msgid="4644150952104035789">"Ekran koruyucu olarak ev kontrollerinize hızla erişin"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 6563034..0d97121 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Показати всі"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Увімкнути Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Підключено"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Збережено"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"від’єднати"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"активувати"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Автоматично ввімкнути знову завтра"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Такі функції, як швидкий обмін, \"Знайти пристрій\" і визначення місцезнаходження пристрою, використовують Bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth увімкнеться завтра о 5:00"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> заряду акумулятора"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудіопристрій"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнітура"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Запис помилки"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Почати"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Зупинити"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Звіт про помилку"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"На який аспект роботи пристрою вплинула проблема?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Виберіть тип проблеми"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Запис відео з екрана"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 452beaf..888be9c 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"سبھی دیکھیں"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"بلوٹوتھ استعمال کریں"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"منسلک ہے"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"محفوظ ہے"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"غیر منسلک کریں"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"فعال کریں"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"کل دوبارہ خودکار طور پر آن ہوگا"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"فوری اشتراک، میرا آلہ ڈھونڈیں، اور آلہ کے مقام جیسی خصوصیات بلوٹوتھ کا استعمال کرتی ہیں"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"بلوٹوتھ کل صبح 5 بجے آن ہو جائے گا"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> بیٹری"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"آڈیو"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ہیڈ سیٹ"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 7a5cbad..6581db5 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Hammasi"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth ishlatish"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Ulangan"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saqlangan"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"uzish"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"faollashtirish"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Ertaga yana avtomatik yoqilsin"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Tezkor ulashuv, Qurilmamni top va qurilma geolokatsiyasi kabi funksiyalar Bluetooth ishlatadi"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth ertaga soat 5 da yoqiladi"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Batareya quvvati: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Garnitura"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Yozib olishda xato"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Boshlash"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Toʻxtatish"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Xatoliklar hisoboti"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Qurilma ishlashining qaysi qismiga taʼsir qildi?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Muammo turini tanlang"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekran yozuvi"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 85421ee..cce810e 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Xem tất cả"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bật Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Đã kết nối"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Đã lưu"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ngắt kết nối"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"kích hoạt"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Tự động bật lại vào ngày mai"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Các tính năng như Chia sẻ nhanh, Tìm thiết bị của tôi và dịch vụ vị trí trên thiết bị có sử dụng Bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth sẽ bật vào ngày mai lúc 5 giờ sáng"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> pin"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Âm thanh"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Tai nghe"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Ghi lại vấn đề"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Bắt đầu"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Dừng"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Báo cáo lỗi"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Bạn gặp loại vấn đề gì khi dùng thiết bị?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Chọn loại vấn đề"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ghi màn hình"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index d23a0fd..f683133 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"查看全部"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"使用蓝牙"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"已连接"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"已保存"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"断开连接"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"启用"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"明天自动重新开启"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"“快速分享”“查找我的设备”“设备位置信息”等功能会使用蓝牙"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"蓝牙将于明天早晨 5 点开启"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> 的电量"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"音频"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"耳机"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"录制问题"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"开始"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"停止"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"错误报告"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"设备体验的哪个方面受到影响?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"选择问题类型"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"屏幕录制"</string>
@@ -748,7 +755,7 @@
     <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"未找到任何快捷键"</string>
     <string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"系统"</string>
     <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"输入"</string>
-    <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"已打开的应用"</string>
+    <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"打开应用"</string>
     <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"当前应用"</string>
     <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"目前显示的是搜索结果"</string>
     <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"目前显示的是系统快捷键"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 04565e5..e6fcb7f 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"查看全部"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"使用藍牙"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"已連接"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"已儲存"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"解除連結"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"啟動"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"明天自動重新開啟"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"「快速共享」、「尋找我的裝置」和裝置位置等功能都會使用藍牙"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"藍牙將於明天上午 5 時開啟"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"電量:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"音訊"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"耳機"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"錄製問題"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"開始"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"停止"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"錯誤報告"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"哪些裝置使用體驗受影響?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"選取問題類型"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"螢幕錄影"</string>
@@ -1303,5 +1310,5 @@
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"鍵盤背光"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"第 %1$d 級,共 %2$d 級"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"智能家居"</string>
-    <string name="home_controls_dream_description" msgid="4644150952104035789">"在螢幕保護程式畫面上快速存取家居控制功能"</string>
+    <string name="home_controls_dream_description" msgid="4644150952104035789">"在螢幕保護程式畫面上控制智能家居"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 11d2c3e..b02bf81 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"查看全部"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"使用藍牙"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"已連線"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"已儲存"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"取消連結"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"啟用"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"明天自動重新開啟"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"藍牙會用於快速分享、「尋找我的裝置」,以及裝置位置資訊等功能"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"藍牙將在明天早上 5 點開啟"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"電量:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"音訊"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"耳機"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"記錄問題"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"開始"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"停止"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"錯誤報告"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"哪些裝置使用體驗受到影響?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"選取問題類型"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"螢幕錄影"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 6f87762..a4d66a4 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -270,12 +270,20 @@
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Buka konke"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Sebenzisa i-Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Ixhunyiwe"</string>
+    <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) -->
+    <skip />
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Ilondoloziwe"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"nqamula"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"yenza kusebenze"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Vula ngokuzenzekela futhi kusasa"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Izakhi ezifana Nokwabelana Ngokushesha, okuthi Thola Idivayisi Yami, kanye nendawo yedivayisi zisebenzisa i-Bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"I-Bluetooth izovulwa kusasa ngo-5 AM"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) -->
+    <skip />
+    <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ibhethri"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Umsindo"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ihedisethi"</string>
@@ -350,8 +358,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Rekhoda Inkinga"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Qala"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Misa"</string>
-    <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) -->
-    <skip />
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Umbiko Wesiphazamisi"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Kuthinteke yiphi ingxenye yokusebenzisa idivayisi?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Khetha uhlobo lwenkinga"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Irekhodi lesikrini"</string>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index 458a21c5..75d925d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -107,7 +107,10 @@
         }
     }
 
-    private void updateMessageAreaVisibility() {
+    /**
+     * Determines whether to show the message area controlled by MessageAreaController.
+     */
+    public void updateMessageAreaVisibility() {
         if (mMessageAreaController == null) return;
         if (Flags.revampedBouncerMessages()) {
             mMessageAreaController.disable();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
index 558679e..3ef3418 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -118,6 +118,12 @@
     }
 
     @Override
+    public void updateMessageAreaVisibility() {
+        if (mMessageAreaController == null) return;
+        mMessageAreaController.setIsVisible(true);
+    }
+
+    @Override
     void resetState() {
         super.resetState();
         if (DEBUG) Log.v(TAG, "Resetting state");
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
index cb1c4b30..46225c7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
@@ -115,6 +115,12 @@
     }
 
     @Override
+    public void updateMessageAreaVisibility() {
+        if (mMessageAreaController == null) return;
+        mMessageAreaController.setIsVisible(true);
+    }
+
+    @Override
     public void onResume(int reason) {
         super.onResume(reason);
         if (mShowDefaultMessage) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index 61d1c71..4a60d19 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -323,7 +323,7 @@
         overlayParams = updatedOverlayParams
         sensorBounds = updatedOverlayParams.sensorBounds
         getTouchOverlay()?.let {
-            if (addViewRunnable != null) {
+            if (addViewRunnable == null) {
                 // Only updateViewLayout if there's no pending view to add to WM.
                 // If there is a pending view, that means the view hasn't been added yet so there's
                 // no need to update any layouts. Instead the correct params will be used when the
diff --git a/packages/SystemUI/src/com/android/systemui/common/coroutine/ConflatedCallbackFlow.kt b/packages/SystemUI/src/com/android/systemui/common/coroutine/ConflatedCallbackFlow.kt
index d4a1f74..0c181e9 100644
--- a/packages/SystemUI/src/com/android/systemui/common/coroutine/ConflatedCallbackFlow.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/coroutine/ConflatedCallbackFlow.kt
@@ -16,14 +16,14 @@
 
 package com.android.systemui.common.coroutine
 
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow as wrapped
 import kotlin.experimental.ExperimentalTypeInference
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.Channel
 import kotlinx.coroutines.channels.ProducerScope
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.buffer
 import kotlinx.coroutines.flow.callbackFlow
 
+@Deprecated("Use com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow instead")
 object ConflatedCallbackFlow {
 
     /**
@@ -32,9 +32,15 @@
      * consumer(s) of the values in the flow), the values are buffered and, if the buffer fills up,
      * we drop the oldest values automatically instead of suspending the producer.
      */
-    @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
-    @OptIn(ExperimentalTypeInference::class, ExperimentalCoroutinesApi::class)
+    @Deprecated(
+        "Use com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow instead",
+        ReplaceWith(
+            "conflatedCallbackFlow",
+            "com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow"
+        )
+    )
+    @OptIn(ExperimentalTypeInference::class)
     fun <T> conflatedCallbackFlow(
         @BuilderInference block: suspend ProducerScope<T>.() -> Unit,
-    ): Flow<T> = callbackFlow(block).buffer(capacity = Channel.CONFLATED)
+    ): Flow<T> = wrapped(block)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractor.kt b/packages/SystemUI/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractor.kt
index 5f6ff82..638af58 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractor.kt
@@ -56,6 +56,13 @@
     val naturalMaxBounds: Flow<Rect> =
         repository.configurationValues.map { it.naturalScreenBounds }.distinctUntilChanged()
 
+    /**
+     * The layout direction. Will be either `View#LAYOUT_DIRECTION_LTR` or
+     * `View#LAYOUT_DIRECTION_RTL`.
+     */
+    val layoutDirection: Flow<Int> =
+        repository.configurationValues.map { it.layoutDirection }.distinctUntilChanged()
+
     /** Given [resourceId], emit the dimension pixel size on config change */
     fun dimensionPixelSize(resourceId: Int): Flow<Int> {
         return onAnyConfigurationChange.mapLatest { repository.getDimensionPixelSize(resourceId) }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 165de7c..21af0a0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -1373,7 +1373,7 @@
     private final Lazy<DreamViewModel> mDreamViewModel;
     private final Lazy<CommunalTransitionViewModel> mCommunalTransitionViewModel;
     private RemoteAnimationTarget mRemoteAnimationTarget;
-    private Boolean mShowCommunalByDefault;
+    private boolean mShowCommunalByDefault = false;
 
     private final Lazy<WindowManagerLockscreenVisibilityManager> mWmLockscreenVisibilityManager;
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt
index 80e94a2..20b7b2a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt
@@ -23,12 +23,14 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.KeyguardSurfaceBehindModel
 import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor
+import com.android.systemui.util.kotlin.sample
 import com.android.systemui.util.kotlin.toPx
 import dagger.Lazy
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
 
 /**
  * Distance over which the surface behind the keyguard is animated in during a Y-translation
@@ -96,13 +98,21 @@
             .distinctUntilChanged()
 
     /**
+     * Whether a notification launch animation is running when we're not already in the GONE state.
+     */
+    private val isNotificationLaunchAnimationRunningOnKeyguard =
+        notificationLaunchInteractor.isLaunchAnimationRunning
+            .sample(transitionInteractor.finishedKeyguardState)
+            .map { it != KeyguardState.GONE }
+
+    /**
      * Whether we're animating the surface, or a notification launch animation is running (which
      * means we're going to animate the surface, even if animators aren't yet running).
      */
     val isAnimatingSurface =
         combine(
             repository.isAnimatingSurface,
-            notificationLaunchInteractor.isLaunchAnimationRunning
+            isNotificationLaunchAnimationRunningOnKeyguard,
         ) { animatingSurface, animatingLaunch ->
             animatingSurface || animatingLaunch
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
index 6255f0d4..7178e1b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
@@ -36,7 +36,6 @@
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.plugins.clocks.ClockController
-import com.android.systemui.shared.clocks.DEFAULT_CLOCK_ID
 import kotlinx.coroutines.launch
 
 object KeyguardClockViewBinder {
@@ -76,13 +75,13 @@
                 }
                 launch {
                     if (!MigrateClocksToBlueprint.isEnabled) return@launch
-                    viewModel.clockShouldBeCentered.collect { clockShouldBeCentered ->
+                    viewModel.clockShouldBeCentered.collect {
                         viewModel.currentClock.value?.let {
-                            // Weather clock also has hasCustomPositionUpdatedAnimation as true
-                            // TODO(b/323020908): remove ID check
+                            // TODO(b/301502635): remove "!it.config.useCustomClockScene" when
+                            // migrate clocks to blueprint is fully rolled out
                             if (
                                 it.largeClock.config.hasCustomPositionUpdatedAnimation &&
-                                    it.config.id == DEFAULT_CLOCK_ID
+                                    !it.config.useCustomClockScene
                             ) {
                                 blueprintInteractor.refreshBlueprint(Type.DefaultClockStepping)
                             } else {
@@ -93,12 +92,9 @@
                 }
                 launch {
                     if (!MigrateClocksToBlueprint.isEnabled) return@launch
-                    viewModel.isAodIconsVisible.collect { isAodIconsVisible ->
+                    viewModel.isAodIconsVisible.collect {
                         viewModel.currentClock.value?.let {
-                            // Weather clock also has hasCustomPositionUpdatedAnimation as true
-                            if (
-                                viewModel.useLargeClock && it.config.id == "DIGITAL_CLOCK_WEATHER"
-                            ) {
+                            if (viewModel.useLargeClock && it.config.useCustomClockScene) {
                                 blueprintInteractor.refreshBlueprint(Type.DefaultTransition)
                             }
                         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index 5ee35e4f..cc54920 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -30,6 +30,9 @@
 import android.view.ViewGroup.OnHierarchyChangeListener
 import android.view.ViewPropertyAnimator
 import android.view.WindowInsets
+import androidx.activity.OnBackPressedDispatcher
+import androidx.activity.OnBackPressedDispatcherOwner
+import androidx.activity.setViewTreeOnBackPressedDispatcherOwner
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.app.animation.Interpolators
@@ -49,6 +52,7 @@
 import com.android.systemui.keyguard.KeyguardViewMediator
 import com.android.systemui.keyguard.MigrateClocksToBlueprint
 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
+import com.android.systemui.keyguard.shared.ComposeLockscreen
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
@@ -125,6 +129,21 @@
         disposables +=
             view.repeatWhenAttached {
                 repeatOnLifecycle(Lifecycle.State.CREATED) {
+                    if (ComposeLockscreen.isEnabled) {
+                        view.setViewTreeOnBackPressedDispatcherOwner(
+                            object : OnBackPressedDispatcherOwner {
+                                override val onBackPressedDispatcher =
+                                    OnBackPressedDispatcher().apply {
+                                        setOnBackInvokedDispatcher(
+                                            view.viewRootImpl.onBackInvokedDispatcher
+                                        )
+                                    }
+
+                                override val lifecycle: Lifecycle =
+                                    [email protected]
+                            }
+                        )
+                    }
                     launch {
                         occludingAppDeviceEntryMessageViewModel.message.collect { biometricMessage
                             ->
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt
index 06a0c72..5a559fc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt
@@ -18,61 +18,29 @@
 package com.android.systemui.keyguard.ui.viewmodel
 
 import android.graphics.Color
-import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor.Companion.TRANSITION_DURATION_MS
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
-import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
-import com.android.wm.shell.animation.Interpolators
 import javax.inject.Inject
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.merge
 
 @ExperimentalCoroutinesApi
 class AlternateBouncerViewModel
 @Inject
 constructor(
     private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
-    animationFlow: KeyguardTransitionAnimationFlow,
+    keyguardTransitionInteractor: KeyguardTransitionInteractor,
 ) {
     // When we're fully transitioned to the AlternateBouncer, the alpha of the scrim should be:
     private val alternateBouncerScrimAlpha = .66f
-    private val toAlternateBouncerTransition =
-        animationFlow
-            .setup(
-                duration = TRANSITION_DURATION_MS,
-                from = null,
-                to = ALTERNATE_BOUNCER,
-            )
-            .sharedFlow(
-                duration = TRANSITION_DURATION_MS,
-                onStep = { it },
-                onFinish = { 1f },
-                // Reset on cancel
-                onCancel = { 0f },
-                interpolator = Interpolators.FAST_OUT_SLOW_IN,
-            )
-    private val fromAlternateBouncerTransition =
-        animationFlow
-            .setup(
-                TRANSITION_DURATION_MS,
-                from = ALTERNATE_BOUNCER,
-                to = null,
-            )
-            .sharedFlow(
-                duration = TRANSITION_DURATION_MS,
-                onStep = { 1f - it },
-                // Reset on cancel
-                onCancel = { 0f },
-                interpolator = Interpolators.FAST_OUT_SLOW_IN,
-            )
 
     /** Progress to a fully transitioned alternate bouncer. 1f represents fully transitioned. */
     val transitionToAlternateBouncerProgress =
-        merge(fromAlternateBouncerTransition, toAlternateBouncerTransition)
+        keyguardTransitionInteractor.transitionValue(ALTERNATE_BOUNCER)
 
     val forcePluginOpen: Flow<Boolean> =
         transitionToAlternateBouncerProgress.map { it > 0f }.distinctUntilChanged()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
index f6da033..a6d3312 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
@@ -118,8 +118,7 @@
                 currentClock
             ) { isLargeClockVisible, clockShouldBeCentered, shadeMode, currentClock ->
                 val shouldUseSplitShade = shadeMode == ShadeMode.Split
-                // TODO(b/326098079): make id a constant field in config
-                if (currentClock?.config?.id == "DIGITAL_CLOCK_WEATHER") {
+                if (currentClock?.config?.useCustomClockScene == true) {
                     val weatherClockLayout =
                         when {
                             shouldUseSplitShade && clockShouldBeCentered ->
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt
index e8d3274..5432a18 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt
@@ -69,7 +69,11 @@
     private val mediaFlags: MediaFlags,
     private val mediaFilterRepository: MediaFilterRepository,
 ) : MediaDataManager.Listener {
-    lateinit var mediaDataManager: MediaDataManager
+    /** Non-UI listeners to media changes. */
+    private val _listeners: MutableSet<MediaDataProcessor.Listener> = mutableSetOf()
+    val listeners: Set<MediaDataProcessor.Listener>
+        get() = _listeners.toSet()
+    lateinit var mediaDataProcessor: MediaDataProcessor
 
     // Ensure the field (and associated reference) isn't removed during optimization.
     @KeepForWeakReference
@@ -113,6 +117,9 @@
         mediaFilterRepository.addMediaDataLoadingState(
             MediaDataLoadingModel.Loaded(data.instanceId)
         )
+
+        // Notify listeners
+        listeners.forEach { it.onMediaDataLoaded(key, oldKey, data) }
     }
 
     override fun onSmartspaceMediaDataLoaded(
@@ -171,6 +178,20 @@
                 mediaFilterRepository.addMediaDataLoadingState(
                     MediaDataLoadingModel.Loaded(lastActiveId)
                 )
+                listeners.forEach { listener ->
+                    getKey(lastActiveId)?.let { lastActiveKey ->
+                        listener.onMediaDataLoaded(
+                            lastActiveKey,
+                            lastActiveKey,
+                            mediaData,
+                            receivedSmartspaceCardLatency =
+                                (systemClock.currentTimeMillis() -
+                                        data.headphoneConnectionTimeMillis)
+                                    .toInt(),
+                            isSsReactivated = true
+                        )
+                    }
+                }
             }
         } else if (data.isActive) {
             // Mark to prioritize Smartspace card if no recent media.
@@ -189,6 +210,7 @@
         mediaFilterRepository.setRecommendationsLoadingState(
             SmartspaceMediaLoadingModel.Loaded(key, shouldPrioritizeMutable)
         )
+        listeners.forEach { it.onSmartspaceMediaDataLoaded(key, data, shouldPrioritizeMutable) }
     }
 
     override fun onMediaDataRemoved(key: String) {
@@ -198,6 +220,8 @@
                 mediaFilterRepository.addMediaDataLoadingState(
                     MediaDataLoadingModel.Removed(instanceId)
                 )
+                // Only notify listeners if something actually changed
+                listeners.forEach { it.onMediaDataRemoved(key) }
             }
         }
     }
@@ -212,6 +236,11 @@
                 mediaFilterRepository.addMediaDataLoadingState(
                     MediaDataLoadingModel.Loaded(lastActiveId, immediately)
                 )
+                listeners.forEach { listener ->
+                    getKey(lastActiveId)?.let { lastActiveKey ->
+                        listener.onMediaDataLoaded(lastActiveKey, lastActiveKey, it, immediately)
+                    }
+                }
             }
         }
 
@@ -227,6 +256,7 @@
         mediaFilterRepository.setRecommendationsLoadingState(
             SmartspaceMediaLoadingModel.Removed(key, immediately)
         )
+        listeners.forEach { it.onSmartspaceMediaDataRemoved(key, immediately) }
     }
 
     @VisibleForTesting
@@ -240,6 +270,7 @@
                 mediaFilterRepository.addMediaDataLoadingState(
                     MediaDataLoadingModel.Removed(data.instanceId)
                 )
+                listeners.forEach { listener -> listener.onMediaDataRemoved(key) }
             }
         }
     }
@@ -247,6 +278,7 @@
     @VisibleForTesting
     internal fun handleUserSwitched() {
         // If the user changes, remove all current MediaData objects.
+        val listenersCopy = listeners
         val keyCopy = mediaFilterRepository.selectedUserEntries.value.keys.toMutableList()
         // Clear the list first and update loading state to remove media from UI.
         mediaFilterRepository.clearSelectedUserMedia()
@@ -255,6 +287,9 @@
             mediaFilterRepository.addMediaDataLoadingState(
                 MediaDataLoadingModel.Removed(instanceId)
             )
+            getKey(instanceId)?.let {
+                listenersCopy.forEach { listener -> listener.onMediaDataRemoved(it) }
+            }
         }
 
         mediaFilterRepository.allUserEntries.value.forEach { (key, data) ->
@@ -268,6 +303,7 @@
                 mediaFilterRepository.addMediaDataLoadingState(
                     MediaDataLoadingModel.Loaded(data.instanceId)
                 )
+                listenersCopy.forEach { listener -> listener.onMediaDataLoaded(key, null, data) }
             }
         }
     }
@@ -279,7 +315,7 @@
         mediaEntries.forEach { (key, data) ->
             if (mediaFilterRepository.selectedUserEntries.value.containsKey(data.instanceId)) {
                 // Force updates to listeners, needed for re-activated card
-                mediaDataManager.setInactive(key, timedOut = true, forceUpdate = true)
+                mediaDataProcessor.setInactive(key, timedOut = true, forceUpdate = true)
             }
         }
         val smartspaceMediaData = mediaFilterRepository.smartspaceMediaData.value
@@ -301,7 +337,7 @@
 
             if (mediaFlags.isPersistentSsCardEnabled()) {
                 mediaFilterRepository.setRecommendation(smartspaceMediaData.copy(isActive = false))
-                mediaDataManager.setRecommendationInactive(smartspaceMediaData.targetId)
+                mediaDataProcessor.setRecommendationInactive(smartspaceMediaData.targetId)
             } else {
                 mediaFilterRepository.setRecommendation(
                     EMPTY_SMARTSPACE_MEDIA_DATA.copy(
@@ -309,7 +345,7 @@
                         instanceId = smartspaceMediaData.instanceId,
                     )
                 )
-                mediaDataManager.dismissSmartspaceRecommendation(
+                mediaDataProcessor.dismissSmartspaceRecommendation(
                     smartspaceMediaData.targetId,
                     delay = 0L,
                 )
@@ -317,6 +353,12 @@
         }
     }
 
+    /** Add a listener for filtered [MediaData] changes */
+    fun addListener(listener: MediaDataProcessor.Listener) = _listeners.add(listener)
+
+    /** Remove a listener that was registered with addListener */
+    fun removeListener(listener: MediaDataProcessor.Listener) = _listeners.remove(listener)
+
     /**
      * Return the time since last active for the most-recent media.
      *
@@ -336,6 +378,16 @@
         return sortedEntries[lastActiveInstanceId]?.let { now - it.lastActive } ?: Long.MAX_VALUE
     }
 
+    private fun getKey(instanceId: InstanceId): String? {
+        val allEntries = mediaFilterRepository.allUserEntries.value
+        val filteredEntries = allEntries.filter { (_, data) -> data.instanceId == instanceId }
+        return if (filteredEntries.isNotEmpty()) {
+            filteredEntries.keys.first()
+        } else {
+            null
+        }
+    }
+
     companion object {
         /**
          * Maximum age of a media control to re-activate on smartspace signal. If there is no media
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
index c7cfb0b..0e2814b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
@@ -35,6 +35,7 @@
 import com.android.settingslib.media.LocalMediaManager
 import com.android.settingslib.media.MediaDevice
 import com.android.settingslib.media.PhoneMediaDevice
+import com.android.settingslib.media.flags.Flags
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.media.controls.shared.model.MediaData
@@ -178,7 +179,9 @@
             bgExecutor.execute {
                 if (!started) {
                     localMediaManager.registerCallback(this)
-                    localMediaManager.startScan()
+                    if (!Flags.removeUnnecessaryRouteScanning()) {
+                        localMediaManager.startScan()
+                    }
                     muteAwaitConnectionManager.startListening()
                     playbackType = controller?.playbackInfo?.playbackType ?: PLAYBACK_TYPE_UNKNOWN
                     playbackVolumeControlId = controller?.playbackInfo?.volumeControlId
@@ -195,7 +198,9 @@
                 if (started) {
                     started = false
                     controller?.unregisterCallback(this)
-                    localMediaManager.stopScan()
+                    if (!Flags.removeUnnecessaryRouteScanning()) {
+                        localMediaManager.stopScan()
+                    }
                     localMediaManager.unregisterCallback(this)
                     muteAwaitConnectionManager.stopListening()
                     configurationController.removeCallback(configListener)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
index c3ba913..b04e938 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
@@ -37,6 +37,7 @@
 import com.android.systemui.media.controls.shared.model.MediaCommonModel
 import com.android.systemui.media.controls.shared.model.MediaDataLoadingModel
 import com.android.systemui.media.controls.shared.model.SmartspaceMediaLoadingModel
+import com.android.systemui.media.controls.util.MediaControlsRefactorFlag
 import com.android.systemui.media.controls.util.MediaFlags
 import java.io.PrintWriter
 import javax.inject.Inject
@@ -156,13 +157,19 @@
             mediaDataProcessor.onSessionDestroyed(key)
         }
         mediaResumeListener.setManager(this)
-        mediaDataFilter.mediaDataManager = this
+        mediaDataFilter.mediaDataProcessor = mediaDataProcessor
     }
 
-    override fun setInactive(key: String, timedOut: Boolean, forceUpdate: Boolean) {
-        mediaDataProcessor.setInactive(key, timedOut, forceUpdate)
+    override fun addListener(listener: MediaDataManager.Listener) {
+        mediaDataFilter.addListener(listener)
     }
 
+    override fun removeListener(listener: MediaDataManager.Listener) {
+        mediaDataFilter.removeListener(listener)
+    }
+
+    override fun setInactive(key: String, timedOut: Boolean, forceUpdate: Boolean) = unsupported
+
     override fun onNotificationAdded(key: String, sbn: StatusBarNotification) {
         mediaDataProcessor.onNotificationAdded(key, sbn)
     }
@@ -207,9 +214,7 @@
         return mediaDataProcessor.dismissSmartspaceRecommendation(key, delay)
     }
 
-    override fun setRecommendationInactive(key: String) {
-        mediaDataProcessor.setRecommendationInactive(key)
-    }
+    override fun setRecommendationInactive(key: String) = unsupported
 
     override fun onNotificationRemoved(key: String) {
         mediaDataProcessor.onNotificationRemoved(key)
@@ -240,4 +245,12 @@
     override fun dump(pw: PrintWriter, args: Array<out String>) {
         mediaDeviceManager.dump(pw)
     }
+
+    companion object {
+        val unsupported: Nothing
+            get() =
+                error(
+                    "Code path not supported when ${MediaControlsRefactorFlag.FLAG_NAME} is enabled"
+                )
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
new file mode 100644
index 0000000..14a9179
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.controls.ui.binder
+
+import android.widget.ImageButton
+import androidx.constraintlayout.widget.ConstraintSet
+import com.android.systemui.res.R
+
+object MediaControlViewBinder {
+
+    fun setVisibleAndAlpha(set: ConstraintSet, resId: Int, visible: Boolean) {
+        setVisibleAndAlpha(set, resId, visible, ConstraintSet.GONE)
+    }
+
+    private fun setVisibleAndAlpha(
+        set: ConstraintSet,
+        resId: Int,
+        visible: Boolean,
+        notVisibleValue: Int
+    ) {
+        set.setVisibility(resId, if (visible) ConstraintSet.VISIBLE else notVisibleValue)
+        set.setAlpha(resId, if (visible) 1.0f else 0.0f)
+    }
+
+    fun updateSeekBarVisibility(constraintSet: ConstraintSet, isSeekBarEnabled: Boolean) {
+        if (isSeekBarEnabled) {
+            constraintSet.setVisibility(R.id.media_progress_bar, ConstraintSet.VISIBLE)
+            constraintSet.setAlpha(R.id.media_progress_bar, 1.0f)
+        } else {
+            constraintSet.setVisibility(R.id.media_progress_bar, ConstraintSet.INVISIBLE)
+            constraintSet.setAlpha(R.id.media_progress_bar, 0.0f)
+        }
+    }
+
+    fun setSemanticButtonVisibleAndAlpha(
+        button: ImageButton,
+        expandedSet: ConstraintSet,
+        collapsedSet: ConstraintSet,
+        visible: Boolean,
+        notVisibleValue: Int,
+        showInCollapsed: Boolean
+    ) {
+        if (notVisibleValue == ConstraintSet.INVISIBLE) {
+            // Since time views should appear instead of buttons.
+            button.isFocusable = visible
+            button.isClickable = visible
+        }
+        setVisibleAndAlpha(expandedSet, button.id, visible, notVisibleValue)
+        setVisibleAndAlpha(collapsedSet, button.id, visible = visible && showInCollapsed)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
index b315cac..7fced5f8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
@@ -16,41 +16,73 @@
 
 package com.android.systemui.media.controls.ui.controller
 
+import android.animation.Animator
+import android.animation.AnimatorInflater
+import android.animation.AnimatorSet
 import android.content.Context
 import android.content.res.Configuration
+import android.graphics.Color
+import android.graphics.Paint
+import android.graphics.drawable.Drawable
+import android.provider.Settings
+import android.view.View
+import android.view.animation.Interpolator
 import androidx.annotation.VisibleForTesting
 import androidx.constraintlayout.widget.ConstraintSet
 import androidx.constraintlayout.widget.ConstraintSet.MATCH_CONSTRAINT
+import com.android.app.animation.Interpolators
 import com.android.app.tracing.traceSection
+import com.android.systemui.Flags
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.media.controls.ui.animation.ColorSchemeTransition
+import com.android.systemui.media.controls.ui.animation.MetadataAnimationHandler
+import com.android.systemui.media.controls.ui.binder.MediaControlViewBinder
+import com.android.systemui.media.controls.ui.binder.SeekBarObserver
 import com.android.systemui.media.controls.ui.controller.MediaCarouselController.Companion.calculateAlpha
 import com.android.systemui.media.controls.ui.view.GutsViewHolder
 import com.android.systemui.media.controls.ui.view.MediaHostState
 import com.android.systemui.media.controls.ui.view.MediaViewHolder
 import com.android.systemui.media.controls.ui.view.RecommendationViewHolder
+import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel
+import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel
 import com.android.systemui.media.controls.util.MediaFlags
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.surfaceeffects.PaintDrawCallback
+import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect
+import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffectView
+import com.android.systemui.surfaceeffects.ripple.MultiRippleController
+import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseAnimationConfig
+import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseController
+import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseShader
+import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseView
 import com.android.systemui.util.animation.MeasurementInput
 import com.android.systemui.util.animation.MeasurementOutput
 import com.android.systemui.util.animation.TransitionLayout
 import com.android.systemui.util.animation.TransitionLayoutController
 import com.android.systemui.util.animation.TransitionViewState
+import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.settings.GlobalSettings
 import java.lang.Float.max
 import java.lang.Float.min
+import java.util.Random
 import javax.inject.Inject
 
 /**
  * A class responsible for controlling a single instance of a media player handling interactions
  * with the view instance and keeping the media view states up to date.
  */
-class MediaViewController
+open class MediaViewController
 @Inject
 constructor(
     private val context: Context,
     private val configurationController: ConfigurationController,
     private val mediaHostStatesManager: MediaHostStatesManager,
     private val logger: MediaViewLogger,
+    private val seekBarViewModel: SeekBarViewModel,
+    @Main private val mainExecutor: DelayableExecutor,
     private val mediaFlags: MediaFlags,
+    private val globalSettings: GlobalSettings,
 ) {
 
     /**
@@ -131,6 +163,72 @@
             return transitionLayout?.translationY ?: 0.0f
         }
 
+    /** Whether artwork is bound. */
+    var isArtworkBound: Boolean = false
+
+    /** previous background artwork */
+    var prevArtwork: Drawable? = null
+
+    /** Whether scrubbing time can show */
+    var canShowScrubbingTime: Boolean = false
+
+    /** Whether user is touching the seek bar to change the position */
+    var isScrubbing: Boolean = false
+
+    var isSeekBarEnabled: Boolean = false
+
+    /** Not visible value for previous button when scrubbing */
+    private var prevNotVisibleValue = ConstraintSet.GONE
+    private var isPrevButtonAvailable = false
+
+    /** Not visible value for next button when scrubbing */
+    private var nextNotVisibleValue = ConstraintSet.GONE
+    private var isNextButtonAvailable = false
+
+    private lateinit var mediaViewHolder: MediaViewHolder
+    private lateinit var seekBarObserver: SeekBarObserver
+    private lateinit var turbulenceNoiseController: TurbulenceNoiseController
+    private lateinit var loadingEffect: LoadingEffect
+    private lateinit var turbulenceNoiseAnimationConfig: TurbulenceNoiseAnimationConfig
+    private lateinit var noiseDrawCallback: PaintDrawCallback
+    private lateinit var stateChangedCallback: LoadingEffect.AnimationStateChangedCallback
+    internal lateinit var metadataAnimationHandler: MetadataAnimationHandler
+    internal lateinit var colorSchemeTransition: ColorSchemeTransition
+    internal lateinit var multiRippleController: MultiRippleController
+
+    private val scrubbingChangeListener =
+        object : SeekBarViewModel.ScrubbingChangeListener {
+            override fun onScrubbingChanged(scrubbing: Boolean) {
+                if (!mediaFlags.isMediaControlsRefactorEnabled()) return
+                if (isScrubbing == scrubbing) return
+                isScrubbing = scrubbing
+                updateDisplayForScrubbingChange()
+            }
+        }
+
+    private val enabledChangeListener =
+        object : SeekBarViewModel.EnabledChangeListener {
+            override fun onEnabledChanged(enabled: Boolean) {
+                if (!mediaFlags.isMediaControlsRefactorEnabled()) return
+                if (isSeekBarEnabled == enabled) return
+                isSeekBarEnabled = enabled
+                MediaControlViewBinder.updateSeekBarVisibility(expandedLayout, isSeekBarEnabled)
+            }
+        }
+
+    /**
+     * Sets the listening state of the player.
+     *
+     * Should be set to true when the QS panel is open. Otherwise, false. This is a signal to avoid
+     * unnecessary work when the QS panel is closed.
+     *
+     * @param listening True when player should be active. Otherwise, false.
+     */
+    fun setListening(listening: Boolean) {
+        if (!mediaFlags.isMediaControlsRefactorEnabled()) return
+        seekBarViewModel.listening = listening
+    }
+
     /** A callback for config changes */
     private val configurationListener =
         object : ConfigurationController.ConfigurationListener {
@@ -232,6 +330,14 @@
      * Notify this controller that the view has been removed and all listeners should be destroyed
      */
     fun onDestroy() {
+        if (mediaFlags.isMediaControlsRefactorEnabled()) {
+            if (this::seekBarObserver.isInitialized) {
+                seekBarViewModel.progress.removeObserver(seekBarObserver)
+            }
+            seekBarViewModel.removeScrubbingChangeListener(scrubbingChangeListener)
+            seekBarViewModel.removeEnabledChangeListener(enabledChangeListener)
+            seekBarViewModel.onDestroy()
+        }
         mediaHostStatesManager.removeController(this)
         configurationController.removeCallback(configurationListener)
     }
@@ -546,6 +652,178 @@
             )
         }
 
+    fun attachPlayer(mediaViewHolder: MediaViewHolder) {
+        if (!mediaFlags.isMediaControlsRefactorEnabled()) return
+        this.mediaViewHolder = mediaViewHolder
+
+        // Setting up seek bar.
+        seekBarObserver = SeekBarObserver(mediaViewHolder)
+        seekBarViewModel.progress.observeForever(seekBarObserver)
+        seekBarViewModel.attachTouchHandlers(mediaViewHolder.seekBar)
+        seekBarViewModel.setScrubbingChangeListener(scrubbingChangeListener)
+        seekBarViewModel.setEnabledChangeListener(enabledChangeListener)
+
+        val mediaCard = mediaViewHolder.player
+        attach(mediaViewHolder.player, TYPE.PLAYER)
+
+        val turbulenceNoiseView = mediaViewHolder.turbulenceNoiseView
+        turbulenceNoiseController = TurbulenceNoiseController(turbulenceNoiseView)
+
+        multiRippleController = MultiRippleController(mediaViewHolder.multiRippleView)
+
+        // Metadata Animation
+        val titleText = mediaViewHolder.titleText
+        val artistText = mediaViewHolder.artistText
+        val explicitIndicator = mediaViewHolder.explicitIndicator
+        val enter =
+            loadAnimator(
+                mediaCard.context,
+                R.anim.media_metadata_enter,
+                Interpolators.EMPHASIZED_DECELERATE,
+                titleText,
+                artistText,
+                explicitIndicator
+            )
+        val exit =
+            loadAnimator(
+                mediaCard.context,
+                R.anim.media_metadata_exit,
+                Interpolators.EMPHASIZED_ACCELERATE,
+                titleText,
+                artistText,
+                explicitIndicator
+            )
+        metadataAnimationHandler = MetadataAnimationHandler(exit, enter)
+
+        colorSchemeTransition =
+            ColorSchemeTransition(
+                mediaCard.context,
+                mediaViewHolder,
+                multiRippleController,
+                turbulenceNoiseController
+            )
+
+        // For Turbulence noise.
+        val loadingEffectView = mediaViewHolder.loadingEffectView
+        turbulenceNoiseAnimationConfig =
+            createTurbulenceNoiseConfig(
+                loadingEffectView,
+                turbulenceNoiseView,
+                colorSchemeTransition
+            )
+        noiseDrawCallback =
+            object : PaintDrawCallback {
+                override fun onDraw(paint: Paint) {
+                    loadingEffectView.draw(paint)
+                }
+            }
+        stateChangedCallback =
+            object : LoadingEffect.AnimationStateChangedCallback {
+                override fun onStateChanged(
+                    oldState: LoadingEffect.AnimationState,
+                    newState: LoadingEffect.AnimationState
+                ) {
+                    if (newState === LoadingEffect.AnimationState.NOT_PLAYING) {
+                        loadingEffectView.visibility = View.INVISIBLE
+                    } else {
+                        loadingEffectView.visibility = View.VISIBLE
+                    }
+                }
+            }
+    }
+
+    fun updateAnimatorDurationScale() {
+        if (!mediaFlags.isMediaControlsRefactorEnabled()) return
+        if (this::seekBarObserver.isInitialized) {
+            seekBarObserver.animationEnabled =
+                globalSettings.getFloat(Settings.Global.ANIMATOR_DURATION_SCALE, 1f) > 0f
+        }
+    }
+
+    /** update view with the needed UI changes when user touches seekbar. */
+    private fun updateDisplayForScrubbingChange() {
+        mainExecutor.execute {
+            val isTimeVisible = canShowScrubbingTime && isScrubbing
+            MediaControlViewBinder.setVisibleAndAlpha(
+                expandedLayout,
+                mediaViewHolder.scrubbingTotalTimeView.id,
+                isTimeVisible
+            )
+            MediaControlViewBinder.setVisibleAndAlpha(
+                expandedLayout,
+                mediaViewHolder.scrubbingElapsedTimeView.id,
+                isTimeVisible
+            )
+
+            MediaControlViewModel.SEMANTIC_ACTIONS_HIDE_WHEN_SCRUBBING.forEach { id ->
+                val isButtonVisible: Boolean
+                val notVisibleValue: Int
+                when (id) {
+                    R.id.actionPrev -> {
+                        isButtonVisible = isPrevButtonAvailable && !isTimeVisible
+                        notVisibleValue = prevNotVisibleValue
+                    }
+                    R.id.actionNext -> {
+                        isButtonVisible = isNextButtonAvailable && !isTimeVisible
+                        notVisibleValue = nextNotVisibleValue
+                    }
+                    else -> {
+                        isButtonVisible = !isTimeVisible
+                        notVisibleValue = ConstraintSet.GONE
+                    }
+                }
+                MediaControlViewBinder.setSemanticButtonVisibleAndAlpha(
+                    mediaViewHolder.getAction(id),
+                    expandedLayout,
+                    collapsedLayout,
+                    isButtonVisible,
+                    notVisibleValue,
+                    showInCollapsed = true
+                )
+            }
+
+            if (!metadataAnimationHandler.isRunning) {
+                refreshState()
+            }
+        }
+    }
+
+    fun bindSeekBar(onSeek: () -> Unit, onBindSeekBar: (SeekBarViewModel) -> Unit) {
+        if (!mediaFlags.isMediaControlsRefactorEnabled()) return
+        seekBarViewModel.logSeek = onSeek
+        onBindSeekBar.invoke(seekBarViewModel)
+    }
+
+    fun setUpTurbulenceNoise() {
+        if (!mediaFlags.isMediaControlsRefactorEnabled()) return
+        if (Flags.shaderlibLoadingEffectRefactor()) {
+            if (!this::loadingEffect.isInitialized) {
+                loadingEffect =
+                    LoadingEffect(
+                        TurbulenceNoiseShader.Companion.Type.SIMPLEX_NOISE,
+                        turbulenceNoiseAnimationConfig,
+                        noiseDrawCallback,
+                        stateChangedCallback
+                    )
+            }
+            colorSchemeTransition.loadingEffect = loadingEffect
+            loadingEffect.play()
+            mainExecutor.executeDelayed(
+                loadingEffect::finish,
+                MediaControlViewModel.TURBULENCE_NOISE_PLAY_MS_DURATION
+            )
+        } else {
+            turbulenceNoiseController.play(
+                TurbulenceNoiseShader.Companion.Type.SIMPLEX_NOISE,
+                turbulenceNoiseAnimationConfig
+            )
+            mainExecutor.executeDelayed(
+                turbulenceNoiseController::finish,
+                MediaControlViewModel.TURBULENCE_NOISE_PLAY_MS_DURATION
+            )
+        }
+    }
+
     /**
      * Obtain a measurement for a given location. This makes sure that the state is up to date and
      * all widgets know their location. Calling this method may create a measurement if we don't
@@ -801,6 +1079,75 @@
                 applyImmediately = true
             )
         }
+
+    @VisibleForTesting
+    protected open fun loadAnimator(
+        context: Context,
+        animId: Int,
+        motionInterpolator: Interpolator?,
+        vararg targets: View?
+    ): AnimatorSet {
+        val animators = ArrayList<Animator>()
+        for (target in targets) {
+            val animator = AnimatorInflater.loadAnimator(context, animId) as AnimatorSet
+            animator.childAnimations[0].interpolator = motionInterpolator
+            animator.setTarget(target)
+            animators.add(animator)
+        }
+        val result = AnimatorSet()
+        result.playTogether(animators)
+        return result
+    }
+
+    private fun createTurbulenceNoiseConfig(
+        loadingEffectView: LoadingEffectView,
+        turbulenceNoiseView: TurbulenceNoiseView,
+        colorSchemeTransition: ColorSchemeTransition
+    ): TurbulenceNoiseAnimationConfig {
+        val targetView: View =
+            if (Flags.shaderlibLoadingEffectRefactor()) {
+                loadingEffectView
+            } else {
+                turbulenceNoiseView
+            }
+        val width = targetView.width
+        val height = targetView.height
+        val random = Random()
+        return TurbulenceNoiseAnimationConfig(
+            gridCount = 2.14f,
+            TurbulenceNoiseAnimationConfig.DEFAULT_LUMINOSITY_MULTIPLIER,
+            random.nextFloat(),
+            random.nextFloat(),
+            random.nextFloat(),
+            noiseMoveSpeedX = 0.42f,
+            noiseMoveSpeedY = 0f,
+            TurbulenceNoiseAnimationConfig.DEFAULT_NOISE_SPEED_Z,
+            // Color will be correctly updated in ColorSchemeTransition.
+            colorSchemeTransition.accentPrimary.currentColor,
+            screenColor = Color.BLACK,
+            width.toFloat(),
+            height.toFloat(),
+            TurbulenceNoiseAnimationConfig.DEFAULT_MAX_DURATION_IN_MILLIS,
+            easeInDuration = 1350f,
+            easeOutDuration = 1350f,
+            targetView.context.resources.displayMetrics.density,
+            lumaMatteBlendFactor = 0.26f,
+            lumaMatteOverallBrightness = 0.09f,
+            shouldInverseNoiseLuminosity = false
+        )
+    }
+
+    fun setUpPrevButtonInfo(isAvailable: Boolean, notVisibleValue: Int = ConstraintSet.GONE) {
+        if (!mediaFlags.isMediaControlsRefactorEnabled()) return
+        isPrevButtonAvailable = isAvailable
+        prevNotVisibleValue = notVisibleValue
+    }
+
+    fun setUpNextButtonInfo(isAvailable: Boolean, notVisibleValue: Int = ConstraintSet.GONE) {
+        if (!mediaFlags.isMediaControlsRefactorEnabled()) return
+        isNextButtonAvailable = isAvailable
+        nextNotVisibleValue = notVisibleValue
+    }
 }
 
 /** An internal key for the cache of mediaViewStates. This is a subset of the full host state. */
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaActionViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaActionViewModel.kt
index 1e67a77..82099e6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaActionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaActionViewModel.kt
@@ -24,7 +24,8 @@
     val icon: Drawable?,
     val contentDescription: CharSequence?,
     val background: Drawable?,
-    val isVisible: Boolean = true,
+    /** whether action is visible if user is touching seekbar to change position. */
+    val isVisibleWhenScrubbing: Boolean = true,
     val notVisibleValue: Int = ConstraintSet.GONE,
     val showInCollapsed: Boolean,
     val rebindId: Int? = null,
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
index 7c59995..d74506d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
@@ -18,6 +18,7 @@
 
 import android.content.Context
 import android.content.pm.PackageManager
+import android.media.session.MediaController
 import android.media.session.MediaSession.Token
 import android.text.TextUtils
 import android.util.Log
@@ -40,6 +41,7 @@
 import com.android.systemui.monet.Style
 import com.android.systemui.res.R
 import com.android.systemui.util.kotlin.sample
+import java.util.concurrent.Executor
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -51,6 +53,7 @@
 class MediaControlViewModel(
     @Application private val applicationContext: Context,
     @Background private val backgroundDispatcher: CoroutineDispatcher,
+    @Background private val backgroundExecutor: Executor,
     private val interactor: MediaControlInteractor,
     private val logger: MediaUiEventLogger,
 ) {
@@ -124,13 +127,15 @@
                 }
             },
             backgroundCover = model.artwork,
-            appIcon = getAppIcon(model.appIcon, model.isResume, model.packageName),
+            appIcon = model.appIcon,
+            launcherIcon = getIconFromApp(model.packageName),
             useGrayColorFilter = model.appIcon == null || model.isResume,
             artistName = model.artistName ?: "",
             titleName = model.songName ?: "",
             isExplicitVisible = model.showExplicit,
+            shouldAddGradient = wallpaperColors != null,
             colorScheme = scheme,
-            isTimeVisible = canShowScrubbingTimeViews(model.semanticActionButtons),
+            canShowTime = canShowScrubbingTimeViews(model.semanticActionButtons),
             playTurbulenceNoise = playTurbulenceNoise,
             useSemanticActions = model.semanticActionButtons != null,
             actionButtons = toActionViewModels(model),
@@ -146,6 +151,21 @@
             onLongClicked = {
                 logger.logLongPressOpen(model.uid, model.packageName, model.instanceId)
             },
+            onSeek = {
+                logger.logSeek(model.uid, model.packageName, model.instanceId)
+                // TODO (b/330897926) log smartspace card reported (SMARTSPACE_CARD_CLICK_EVENT)
+            },
+            onBindSeekbar = { seekBarViewModel ->
+                if (model.isResume && model.resumeProgress != null) {
+                    seekBarViewModel.updateStaticProgress(model.resumeProgress)
+                } else {
+                    backgroundExecutor.execute {
+                        seekBarViewModel.updateController(
+                            model.token?.let { MediaController(applicationContext, it) }
+                        )
+                    }
+                }
+            }
         )
     }
 
@@ -278,16 +298,16 @@
         model: MediaControlModel,
         mediaAction: MediaAction,
         buttonId: Int,
-        isScrubbingTimeEnabled: Boolean
+        canShowScrubbingTimeViews: Boolean
     ): MediaActionViewModel {
         val showInCollapsed = SEMANTIC_ACTIONS_COMPACT.contains(buttonId)
         val hideWhenScrubbing = SEMANTIC_ACTIONS_HIDE_WHEN_SCRUBBING.contains(buttonId)
-        val shouldHideDueToScrubbing = isScrubbingTimeEnabled && hideWhenScrubbing
+        val shouldHideWhenScrubbing = canShowScrubbingTimeViews && hideWhenScrubbing
         return MediaActionViewModel(
             icon = mediaAction.icon,
             contentDescription = mediaAction.contentDescription,
             background = mediaAction.background,
-            isVisible = !shouldHideDueToScrubbing,
+            isVisibleWhenScrubbing = !shouldHideWhenScrubbing,
             notVisibleValue =
                 if (
                     (buttonId == R.id.actionPrev && model.semanticActionButtons!!.reservePrev) ||
@@ -342,19 +362,6 @@
         action.run()
     }
 
-    private fun getAppIcon(
-        icon: android.graphics.drawable.Icon?,
-        isResume: Boolean,
-        packageName: String
-    ): Icon {
-        if (icon != null && !isResume) {
-            icon.loadDrawable(applicationContext)?.let { drawable ->
-                return Icon.Loaded(drawable, null)
-            }
-        }
-        return getIconFromApp(packageName)
-    }
-
     private fun getIconFromApp(packageName: String): Icon {
         return try {
             Icon.Loaded(applicationContext.packageManager.getApplicationIcon(packageName), null)
@@ -381,17 +388,17 @@
         private const val DISABLED_ALPHA = 0.38f
 
         /** Buttons to show in small player when using semantic actions */
-        private val SEMANTIC_ACTIONS_COMPACT =
+        val SEMANTIC_ACTIONS_COMPACT =
             listOf(R.id.actionPlayPause, R.id.actionPrev, R.id.actionNext)
 
         /**
          * Buttons that should get hidden when we are scrubbing (they will be replaced with the
          * views showing scrubbing time)
          */
-        private val SEMANTIC_ACTIONS_HIDE_WHEN_SCRUBBING = listOf(R.id.actionPrev, R.id.actionNext)
+        val SEMANTIC_ACTIONS_HIDE_WHEN_SCRUBBING = listOf(R.id.actionPrev, R.id.actionNext)
 
         /** Buttons to show in player when using semantic actions. */
-        private val SEMANTIC_ACTIONS_ALL =
+        val SEMANTIC_ACTIONS_ALL =
             listOf(
                 R.id.actionPlayPause,
                 R.id.actionPrev,
@@ -399,5 +406,9 @@
                 R.id.action0,
                 R.id.action1
             )
+
+        const val TURBULENCE_NOISE_PLAY_MS_DURATION = 7500L
+        const val MEDIA_PLAYER_SCRIM_START_ALPHA = 0.25f
+        const val MEDIA_PLAYER_SCRIM_END_ALPHA = 1.0f
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt
index 9029a65..d1014e8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt
@@ -24,13 +24,15 @@
 data class MediaPlayerViewModel(
     val contentDescription: (Boolean) -> CharSequence,
     val backgroundCover: android.graphics.drawable.Icon?,
-    val appIcon: Icon,
+    val appIcon: android.graphics.drawable.Icon?,
+    val launcherIcon: Icon,
     val useGrayColorFilter: Boolean,
     val artistName: CharSequence,
     val titleName: CharSequence,
     val isExplicitVisible: Boolean,
+    val shouldAddGradient: Boolean,
     val colorScheme: ColorScheme,
-    val isTimeVisible: Boolean,
+    val canShowTime: Boolean,
     val playTurbulenceNoise: Boolean,
     val useSemanticActions: Boolean,
     val actionButtons: List<MediaActionViewModel?>,
@@ -38,4 +40,6 @@
     val gutsMenu: GutsViewModel,
     val onClicked: (Expandable) -> Unit,
     val onLongClicked: () -> Unit,
+    val onSeek: () -> Unit,
+    val onBindSeekbar: (SeekBarViewModel) -> Unit,
 )
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/scroll/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/LongScreenshotActivity.java
index 706ac9c..b43a1d2 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/scroll/LongScreenshotActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/LongScreenshotActivity.java
@@ -23,6 +23,7 @@
 import android.content.Intent;
 import android.graphics.Bitmap;
 import android.graphics.HardwareRenderer;
+import android.graphics.Insets;
 import android.graphics.Matrix;
 import android.graphics.RecordingCanvas;
 import android.graphics.Rect;
@@ -38,9 +39,11 @@
 import android.view.Display;
 import android.view.ScrollCaptureResponse;
 import android.view.View;
+import android.view.WindowInsets;
 import android.widget.ImageView;
 
 import androidx.constraintlayout.widget.ConstraintLayout;
+import androidx.core.view.WindowCompat;
 
 import com.android.internal.app.ChooserActivity;
 import com.android.internal.logging.UiEventLogger;
@@ -127,6 +130,10 @@
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+
+        // Enable edge-to-edge explicitly.
+        WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
+
         setContentView(R.layout.long_screenshot);
 
         mPreview = requireViewById(R.id.preview);
@@ -149,6 +156,13 @@
                 (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
                         updateImageDimensions());
 
+        requireViewById(R.id.root).setOnApplyWindowInsetsListener(
+                (view, windowInsets) -> {
+                    Insets insets = windowInsets.getInsets(WindowInsets.Type.systemBars());
+                    view.setPadding(insets.left, insets.top, insets.right, insets.bottom);
+                    return WindowInsets.CONSUMED;
+                });
+
         Intent intent = getIntent();
         mScrollCaptureResponse = intent.getParcelableExtra(EXTRA_CAPTURE_RESPONSE);
         mScreenshotUserHandle = intent.getParcelableExtra(EXTRA_SCREENSHOT_USER_HANDLE,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 00bc752..a763641 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -659,6 +659,9 @@
             mTouchCancelled = true;
         }
         mAmbientState.setSwipingUp(false);
+        if (MigrateClocksToBlueprint.isEnabled()) {
+            mDragDownHelper.stopDragging();
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
index 980f665a..5b76acb 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
@@ -37,6 +37,7 @@
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.shade.shared.model.ShadeMode
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
+import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
 import java.util.concurrent.atomic.AtomicBoolean
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
@@ -64,6 +65,7 @@
     private val footerActionsViewModelFactory: FooterActionsViewModel.Factory,
     private val footerActionsController: FooterActionsController,
     private val sceneInteractor: SceneInteractor,
+    private val unfoldTransitionInteractor: UnfoldTransitionInteractor,
 ) {
     val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
         combine(
@@ -106,6 +108,14 @@
 
     val shadeMode: StateFlow<ShadeMode> = shadeInteractor.shadeMode
 
+    /**
+     * Amount of X-axis translation to apply to various elements as the unfolded foldable is folded
+     * slightly, in pixels.
+     */
+    fun unfoldTranslationX(isOnStartSide: Boolean): Flow<Float> {
+        return unfoldTransitionInteractor.unfoldTranslationX(isOnStartSide)
+    }
+
     /** Notifies that some content in the shade was clicked. */
     fun onContentClicked() {
         if (!isClickable.value) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 519d719..e3db626 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -959,7 +959,7 @@
         anim.start()
     }
 
-    private fun stopDragging() {
+    fun stopDragging() {
         if (startingChild != null) {
             cancelChildExpansion(startingChild!!)
             startingChild = null
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 9e0b16c..0c8518f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -1767,7 +1767,8 @@
      */
     public ExpandableNotificationRow(Context context, AttributeSet attrs) {
         this(context, attrs, context);
-        Log.e(TAG, "This constructor shouldn't be called");
+        // NOTE(b/317503801): Always crash when using the insecure constructor.
+        throw new UnsupportedOperationException("Insecure constructor");
     }
 
     /**
@@ -2804,12 +2805,7 @@
     }
 
     public boolean isExpanded(boolean allowOnKeyguard) {
-        if (DEBUG) {
-            if (!mShowingPublicInitialized && !allowOnKeyguard) {
-                Log.d(TAG, "mShowingPublic is not initialized.");
-            }
-        }
-        return !mShowingPublic && (!mOnKeyguard || allowOnKeyguard)
+        return (!shouldShowPublic()) && (!mOnKeyguard || allowOnKeyguard)
                 && (!hasUserChangedExpansion() && (isSystemExpanded() || isSystemChildExpanded())
                 || isUserExpanded());
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
index 5fbcebd..35afda7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
@@ -33,6 +33,8 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.util.time.SystemClock;
 
+import java.util.concurrent.Executor;
+
 import javax.inject.Inject;
 
 /**
@@ -58,10 +60,22 @@
     }
 
     /**
-     * Inflates a new notificationView. This should not be called twice on this object
+     * Inflates a new notificationView asynchronously, calling the {@code listener} on the main
+     * thread when done. This should not be called twice on this object.
      */
     public void inflate(Context context, ViewGroup parent, NotificationEntry entry,
             RowInflationFinishedListener listener) {
+        inflate(context, parent, entry, null, listener);
+    }
+
+    /**
+     * Inflates a new notificationView asynchronously, calling the {@code listener} on the supplied
+     * {@code listenerExecutor} (or the main thread if null) when done. This should not be called
+     * twice on this object.
+     */
+    @VisibleForTesting
+    public void inflate(Context context, ViewGroup parent, NotificationEntry entry,
+            @Nullable Executor listenerExecutor, RowInflationFinishedListener listener) {
         if (TRACE_ORIGIN) {
             mInflateOrigin = new Throwable("inflate requested here");
         }
@@ -72,7 +86,7 @@
 
         mLogger.logInflateStart(entry);
         mInflateStartTimeMs = mSystemClock.elapsedRealtime();
-        inflater.inflate(R.layout.status_bar_notification_row, parent, this);
+        inflater.inflate(R.layout.status_bar_notification_row, parent, listenerExecutor, this);
     }
 
     private RowAsyncLayoutInflater makeRowInflater(NotificationEntry entry) {
@@ -80,12 +94,12 @@
     }
 
     @VisibleForTesting
-    static class RowAsyncLayoutInflater implements AsyncLayoutFactory {
+    public static class RowAsyncLayoutInflater implements AsyncLayoutFactory {
         private final NotificationEntry mEntry;
         private final SystemClock mSystemClock;
         private final RowInflaterTaskLogger mLogger;
 
-        RowAsyncLayoutInflater(NotificationEntry entry, SystemClock systemClock,
+        public RowAsyncLayoutInflater(NotificationEntry entry, SystemClock systemClock,
                 RowInflaterTaskLogger logger) {
             mEntry = entry;
             mSystemClock = systemClock;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index 5099682..37bbbd0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -66,6 +66,7 @@
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
 import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
+import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
 import com.android.systemui.util.kotlin.BooleanFlowOperators.and
 import com.android.systemui.util.kotlin.BooleanFlowOperators.or
 import com.android.systemui.util.kotlin.FlowDumperImpl
@@ -80,6 +81,7 @@
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.combineTransform
 import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flow
@@ -128,6 +130,7 @@
     private val primaryBouncerToLockscreenTransitionViewModel:
         PrimaryBouncerToLockscreenTransitionViewModel,
     private val aodBurnInViewModel: AodBurnInViewModel,
+    unfoldTransitionInteractor: UnfoldTransitionInteractor,
 ) : FlowDumperImpl(dumpManager) {
     private val statesForConstrainedNotifications: Set<KeyguardState> =
         setOf(AOD, LOCKSCREEN, DOZING, ALTERNATE_BOUNCER, PRIMARY_BOUNCER)
@@ -577,14 +580,20 @@
             .dumpWhileCollecting("translationY")
     }
 
-    /**
-     * The container may need to be translated in the x direction as the keyguard fades out, such as
-     * when swiping open the glanceable hub from the lockscreen.
-     */
+    /** Horizontal translation to apply to the container. */
     val translationX: Flow<Float> =
         merge(
+                // The container may need to be translated along the X axis as the keyguard fades
+                // out, such as when swiping open the glanceable hub from the lockscreen.
                 lockscreenToGlanceableHubTransitionViewModel.notificationTranslationX,
                 glanceableHubToLockscreenTransitionViewModel.notificationTranslationX,
+                if (SceneContainerFlag.isEnabled) {
+                    // The container may need to be translated along the X axis as the unfolded
+                    // foldable is folded slightly.
+                    unfoldTransitionInteractor.unfoldTranslationX(isOnStartSide = false)
+                } else {
+                    emptyFlow()
+                }
             )
             .dumpWhileCollecting("translationX")
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
index ebaeb39..68d54e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
@@ -131,7 +131,7 @@
 
         val runnable = Runnable {
             assistManagerLazy.get().hideAssist()
-            intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
+            intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
             intent.addFlags(flags)
             val result = intArrayOf(ActivityManager.START_CANCELED)
             activityTransitionAnimator.startIntentWithAnimation(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
index a0c5618..5f08afd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
@@ -128,6 +128,8 @@
                                 mobileGroupView,
                                 dotView,
                             )
+
+                            view.requestLayout()
                         }
                     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
index 08ed030..054116d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
@@ -22,6 +22,8 @@
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
 import com.android.systemui.statusbar.pipeline.satellite.data.DeviceBasedSatelliteRepository
 import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
 import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
@@ -41,6 +43,7 @@
     val repo: DeviceBasedSatelliteRepository,
     iconsInteractor: MobileIconsInteractor,
     deviceProvisioningInteractor: DeviceProvisioningInteractor,
+    wifiInteractor: WifiInteractor,
     @Application scope: CoroutineScope,
 ) {
     /** Must be observed by any UI showing Satellite iconography */
@@ -73,6 +76,9 @@
 
     val isDeviceProvisioned: Flow<Boolean> = deviceProvisioningInteractor.isDeviceProvisioned
 
+    val isWifiActive: Flow<Boolean> =
+        wifiInteractor.wifiNetwork.map { it is WifiNetworkModel.Active }
+
     /** When all connections are considered OOS, satellite connectivity is potentially valid */
     val areAllConnectionsOutOfService =
         if (Flags.oemEnabledSatelliteFlag()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
index 40641be..a0291b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
@@ -59,9 +59,10 @@
                 combine(
                     interactor.isSatelliteAllowed,
                     interactor.isDeviceProvisioned,
+                    interactor.isWifiActive,
                     airplaneModeRepository.isAirplaneMode
-                ) { isSatelliteAllowed, isDeviceProvisioned, isAirplaneMode ->
-                    isSatelliteAllowed && isDeviceProvisioned && !isAirplaneMode
+                ) { isSatelliteAllowed, isDeviceProvisioned, isWifiActive, isAirplaneMode ->
+                    isSatelliteAllowed && isDeviceProvisioned && !isWifiActive && !isAirplaneMode
                 }
             }
         }
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 1b56702..0c2abd9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
@@ -153,18 +153,20 @@
             // Use default duration, like we did before AvalancheController existed
             return autoDismissMs
         }
+
         val showingList: MutableList<HeadsUpEntry> = mutableListOf()
         headsUpEntryShowing?.let { showingList.add(it) }
 
+        nextList.sort()
         val entryList = showingList + nextList
-        if (entryList.indexOf(entry) == entryList.size - 1) {
-            // Use default duration if last entry
+        val thisEntryIndex = entryList.indexOf(entry)
+        val nextEntryIndex = thisEntryIndex + 1
+
+        // If last entry, use default duration
+        if (nextEntryIndex >= entryList.size) {
             return autoDismissMs
         }
-
-        nextList.sort()
-        val nextEntry = nextList[0]
-
+        val nextEntry = entryList[nextEntryIndex]
         if (nextEntry.compareNonTimeFields(entry) == -1) {
             // Next entry is higher priority
             return 500
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
index 3522850..37ef1f2 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
@@ -32,8 +32,6 @@
 import com.android.systemui.unfold.data.repository.FoldStateRepositoryImpl
 import com.android.systemui.unfold.data.repository.UnfoldTransitionRepository
 import com.android.systemui.unfold.data.repository.UnfoldTransitionRepositoryImpl
-import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
-import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractorImpl
 import com.android.systemui.unfold.system.SystemUnfoldSharedModule
 import com.android.systemui.unfold.updates.FoldProvider
 import com.android.systemui.unfold.updates.FoldStateProvider
@@ -186,8 +184,6 @@
     interface Bindings {
         @Binds fun bindRepository(impl: UnfoldTransitionRepositoryImpl): UnfoldTransitionRepository
 
-        @Binds fun bindInteractor(impl: UnfoldTransitionInteractorImpl): UnfoldTransitionInteractor
-
         @Binds fun bindFoldStateRepository(impl: FoldStateRepositoryImpl): FoldStateRepository
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/data/repository/UnfoldTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/unfold/data/repository/UnfoldTransitionRepository.kt
index 0d3682c..fbbd2b9 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/data/repository/UnfoldTransitionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/data/repository/UnfoldTransitionRepository.kt
@@ -15,9 +15,11 @@
  */
 package com.android.systemui.unfold.data.repository
 
+import androidx.annotation.FloatRange
 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider
 import com.android.systemui.unfold.data.repository.UnfoldTransitionStatus.TransitionFinished
+import com.android.systemui.unfold.data.repository.UnfoldTransitionStatus.TransitionInProgress
 import com.android.systemui.unfold.data.repository.UnfoldTransitionStatus.TransitionStarted
 import com.android.systemui.util.kotlin.getOrNull
 import java.util.Optional
@@ -42,6 +44,10 @@
 sealed class UnfoldTransitionStatus {
     /** Status that is sent when fold or unfold transition is in started state */
     data object TransitionStarted : UnfoldTransitionStatus()
+    /** Status that is sent while fold or unfold transition is in progress */
+    data class TransitionInProgress(
+        @FloatRange(from = 0.0, to = 1.0) val progress: Float,
+    ) : UnfoldTransitionStatus()
     /** Status that is sent when fold or unfold transition is finished */
     data object TransitionFinished : UnfoldTransitionStatus()
 }
@@ -66,6 +72,10 @@
                             trySend(TransitionStarted)
                         }
 
+                        override fun onTransitionProgress(progress: Float) {
+                            trySend(TransitionInProgress(progress))
+                        }
+
                         override fun onTransitionFinished() {
                             trySend(TransitionFinished)
                         }
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractor.kt
index 3e2e564..03499cb 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractor.kt
@@ -15,40 +15,79 @@
  */
 package com.android.systemui.unfold.domain.interactor
 
+import android.view.View
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.res.R
 import com.android.systemui.unfold.data.repository.UnfoldTransitionRepository
 import com.android.systemui.unfold.data.repository.UnfoldTransitionStatus.TransitionFinished
+import com.android.systemui.unfold.data.repository.UnfoldTransitionStatus.TransitionInProgress
 import com.android.systemui.unfold.data.repository.UnfoldTransitionStatus.TransitionStarted
 import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
 
 /**
  * Contains business-logic related to fold-unfold transitions while interacting with
  * [UnfoldTransitionRepository]
  */
-interface UnfoldTransitionInteractor {
+@SysUISingleton
+class UnfoldTransitionInteractor
+@Inject
+constructor(
+    private val repository: UnfoldTransitionRepository,
+    private val configurationInteractor: ConfigurationInteractor,
+) {
     /** Returns availability of fold/unfold transitions on the device */
     val isAvailable: Boolean
-
-    /** Suspends and waits for a fold/unfold transition to finish */
-    suspend fun waitForTransitionFinish()
-
-    /** Suspends and waits for a fold/unfold transition to start */
-    suspend fun waitForTransitionStart()
-}
-
-class UnfoldTransitionInteractorImpl
-@Inject
-constructor(private val repository: UnfoldTransitionRepository) : UnfoldTransitionInteractor {
-
-    override val isAvailable: Boolean
         get() = repository.isAvailable
 
-    override suspend fun waitForTransitionFinish() {
+    /**
+     * This mapping emits 1 when the device is completely unfolded and 0.0 when the device is
+     * completely folded.
+     */
+    private val unfoldProgress: Flow<Float> =
+        repository.transitionStatus
+            .map { (it as? TransitionInProgress)?.progress ?: 1f }
+            .onStart { emit(1f) }
+            .distinctUntilChanged()
+
+    /**
+     * Amount of X-axis translation to apply to various elements as the unfolded foldable is folded
+     * slightly, in pixels.
+     *
+     * @param isOnStartSide Whether the consumer wishes to get a translation amount that's suitable
+     *   for an element that's on the start-side (left hand-side in left-to-right layouts); if
+     *   `true`, the values will provide positive translations to push the left-hand-side element
+     *   towards the foldable hinge; if `false`, the values will be inverted to provide negative
+     *   translations to push the right-hand-side element towards the foldable hinge. Note that this
+     *   method already accounts for left-to-right vs. right-to-left layout directions.
+     */
+    fun unfoldTranslationX(isOnStartSide: Boolean): Flow<Float> {
+        return combine(
+            unfoldProgress,
+            configurationInteractor.dimensionPixelSize(R.dimen.notification_side_paddings),
+            configurationInteractor.layoutDirection.map {
+                if (it == View.LAYOUT_DIRECTION_RTL) -1 else 1
+            },
+        ) { unfoldedAmount, max, layoutDirectionMultiplier ->
+            val sideMultiplier = if (isOnStartSide) 1 else -1
+            max * (1 - unfoldedAmount) * sideMultiplier * layoutDirectionMultiplier
+        }
+    }
+
+    /** Suspends and waits for a fold/unfold transition to finish */
+    suspend fun waitForTransitionFinish() {
         repository.transitionStatus.filter { it is TransitionFinished }.first()
     }
 
-    override suspend fun waitForTransitionStart() {
+    /** Suspends and waits for a fold/unfold transition to start */
+    suspend fun waitForTransitionStart() {
         repository.transitionStatus.filter { it is TransitionStarted }.first()
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/button/ui/viewmodel/ButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/button/ui/viewmodel/ButtonViewModel.kt
index 754d258..4d11f44 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/button/ui/viewmodel/ButtonViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/button/ui/viewmodel/ButtonViewModel.kt
@@ -1,17 +1,17 @@
 /*
  * 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
+ * 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
+ *      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.
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 package com.android.systemui.volume.panel.component.button.ui.viewmodel
@@ -22,4 +22,5 @@
 data class ButtonViewModel(
     val icon: Icon,
     val label: CharSequence,
+    val isActive: Boolean = true,
 )
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/button/ui/viewmodel/ToggleButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/button/ui/viewmodel/ToggleButtonViewModel.kt
deleted file mode 100644
index 6c47aec..0000000
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/button/ui/viewmodel/ToggleButtonViewModel.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at
- *
- *       http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-
-package com.android.systemui.volume.panel.component.button.ui.viewmodel
-
-import com.android.systemui.common.shared.model.Icon
-
-data class ToggleButtonViewModel(
-    val isChecked: Boolean,
-    val icon: Icon,
-    val label: CharSequence,
-)
-
-fun ToggleButtonViewModel.toButtonViewModel(): ButtonViewModel =
-    ButtonViewModel(icon = icon, label = label)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModel.kt
index 01421f8..ca5aef8 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModel.kt
@@ -21,7 +21,7 @@
 import com.android.settingslib.view.accessibility.domain.interactor.CaptioningInteractor
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.res.R
-import com.android.systemui.volume.panel.component.button.ui.viewmodel.ToggleButtonViewModel
+import com.android.systemui.volume.panel.component.button.ui.viewmodel.ButtonViewModel
 import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
 import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
 import javax.inject.Inject
@@ -43,11 +43,11 @@
     private val uiEventLogger: UiEventLogger,
 ) {
 
-    val buttonViewModel: StateFlow<ToggleButtonViewModel?> =
+    val buttonViewModel: StateFlow<ButtonViewModel?> =
         captioningInteractor.isSystemAudioCaptioningEnabled
             .map { isEnabled ->
-                ToggleButtonViewModel(
-                    isChecked = isEnabled,
+                ButtonViewModel(
+                    isActive = isEnabled,
                     icon =
                         Icon.Resource(
                             if (isEnabled) R.drawable.ic_volume_odi_captions
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioButtonViewModel.kt
index e5c5a65..f7a602e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioButtonViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioButtonViewModel.kt
@@ -16,10 +16,10 @@
 
 package com.android.systemui.volume.panel.component.spatial.ui.viewmodel
 
-import com.android.systemui.volume.panel.component.button.ui.viewmodel.ToggleButtonViewModel
+import com.android.systemui.volume.panel.component.button.ui.viewmodel.ButtonViewModel
 import com.android.systemui.volume.panel.component.spatial.domain.model.SpatialAudioEnabledModel
 
 data class SpatialAudioButtonViewModel(
     val model: SpatialAudioEnabledModel,
-    val button: ToggleButtonViewModel,
+    val button: ButtonViewModel,
 )
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt
index b5e9ed2..4b2d26a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt
@@ -22,8 +22,6 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.res.R
 import com.android.systemui.volume.panel.component.button.ui.viewmodel.ButtonViewModel
-import com.android.systemui.volume.panel.component.button.ui.viewmodel.ToggleButtonViewModel
-import com.android.systemui.volume.panel.component.button.ui.viewmodel.toButtonViewModel
 import com.android.systemui.volume.panel.component.spatial.domain.SpatialAudioAvailabilityCriteria
 import com.android.systemui.volume.panel.component.spatial.domain.interactor.SpatialAudioComponentInteractor
 import com.android.systemui.volume.panel.component.spatial.domain.model.SpatialAudioAvailabilityModel
@@ -53,8 +51,8 @@
     val spatialAudioButton: StateFlow<ButtonViewModel?> =
         interactor.isEnabled
             .map {
-                it.toViewModel(true)
-                    .toButtonViewModel()
+                val isChecked = it is SpatialAudioEnabledModel.SpatialAudioEnabled
+                it.toViewModel(isChecked)
                     .copy(label = context.getString(R.string.volume_panel_spatial_audio_title))
             }
             .stateIn(scope, SharingStarted.Eagerly, null)
@@ -76,8 +74,7 @@
                     }
                     .map { isEnabled ->
                         val isChecked = isEnabled == currentIsEnabled
-                        val buttonViewModel: ToggleButtonViewModel =
-                            isEnabled.toViewModel(isChecked)
+                        val buttonViewModel: ButtonViewModel = isEnabled.toViewModel(isChecked)
                         SpatialAudioButtonViewModel(button = buttonViewModel, model = isEnabled)
                     }
             }
@@ -100,26 +97,26 @@
         scope.launch { interactor.setEnabled(model) }
     }
 
-    private fun SpatialAudioEnabledModel.toViewModel(isChecked: Boolean): ToggleButtonViewModel {
+    private fun SpatialAudioEnabledModel.toViewModel(isChecked: Boolean): ButtonViewModel {
         if (this is SpatialAudioEnabledModel.HeadTrackingEnabled) {
-            return ToggleButtonViewModel(
-                isChecked = isChecked,
+            return ButtonViewModel(
+                isActive = isChecked,
                 icon = Icon.Resource(R.drawable.ic_head_tracking, contentDescription = null),
                 label = context.getString(R.string.volume_panel_spatial_audio_tracking)
             )
         }
 
         if (this is SpatialAudioEnabledModel.SpatialAudioEnabled) {
-            return ToggleButtonViewModel(
-                isChecked = isChecked,
+            return ButtonViewModel(
+                isActive = isChecked,
                 icon = Icon.Resource(R.drawable.ic_spatial_audio, contentDescription = null),
                 label = context.getString(R.string.volume_panel_spatial_audio_fixed)
             )
         }
 
         if (this is SpatialAudioEnabledModel.Disabled) {
-            return ToggleButtonViewModel(
-                isChecked = isChecked,
+            return ButtonViewModel(
+                isActive = isChecked,
                 icon = Icon.Resource(R.drawable.ic_spatial_audio_off, contentDescription = null),
                 label = context.getString(R.string.volume_panel_spatial_audio_off)
             )
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
index a249961..319b615 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
@@ -205,9 +205,9 @@
         when(mClockRegistry.createCurrentClock()).thenReturn(mClockController);
         when(mClockEventController.getClock()).thenReturn(mClockController);
         when(mSmallClockController.getConfig())
-                .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, false, false));
+                .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, false, false, false));
         when(mLargeClockController.getConfig())
-                .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, false, false));
+                .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, false, false, false));
 
         mSliceView = new View(getContext());
         when(mView.findViewById(R.id.keyguard_slice_view)).thenReturn(mSliceView);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index 9d81b96..99b5a4b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -272,9 +272,9 @@
         assertEquals(View.VISIBLE, mFakeDateView.getVisibility());
 
         when(mSmallClockController.getConfig())
-                .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, true, false));
+                .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, true, false, true));
         when(mLargeClockController.getConfig())
-                .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, true, false));
+                .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, true, false, true));
         verify(mClockRegistry).registerClockChangeListener(listenerArgumentCaptor.capture());
         listenerArgumentCaptor.getValue().onCurrentClockChanged();
 
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
index 9b5364e..7151c42 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
@@ -115,6 +115,7 @@
     @Test
     fun onViewAttached() {
         underTest.onViewAttached()
+        verify(keyguardMessageAreaController).setIsVisible(true)
         verify(keyguardMessageAreaController)
             .setMessage(context.resources.getString(R.string.keyguard_enter_your_pin), false)
         verify(keyguardUpdateMonitor)
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
index e71490c..acae913 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
@@ -103,7 +103,9 @@
 
     @Test
     fun onViewAttached() {
+        Mockito.reset(keyguardMessageAreaController)
         underTest.onViewAttached()
+        Mockito.verify(keyguardMessageAreaController).setIsVisible(true)
         Mockito.verify(keyguardUpdateMonitor)
             .registerCallback(any(KeyguardUpdateMonitorCallback::class.java))
         Mockito.verify(keyguardMessageAreaController)
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
index 11fe862..b2828a4 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
@@ -98,7 +98,7 @@
     public void updatePosition_primaryClockAnimation() {
         ClockController mockClock = mock(ClockController.class);
         when(mKeyguardClockSwitchController.getClock()).thenReturn(mockClock);
-        when(mockClock.getConfig()).thenReturn(new ClockConfig("MOCK", "", "", false, true));
+        when(mockClock.getConfig()).thenReturn(new ClockConfig("MOCK", "", "", false, true, false));
 
         mController.updatePosition(10, 15, 20f, true);
 
@@ -113,7 +113,7 @@
     public void updatePosition_alternateClockAnimation() {
         ClockController mockClock = mock(ClockController.class);
         when(mKeyguardClockSwitchController.getClock()).thenReturn(mockClock);
-        when(mockClock.getConfig()).thenReturn(new ClockConfig("MOCK", "", "", true, true));
+        when(mockClock.getConfig()).thenReturn(new ClockConfig("MOCK", "", "", true, true, false));
 
         mController.updatePosition(10, 15, 20f, true);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt
index 3f05bfa..9ccf212 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.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.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -227,4 +228,50 @@
                 { it == KeyguardSurfaceBehindModel(alpha = 0f) },
             )
         }
+
+    @Test
+    fun notificationLaunchFromLockscreen_isAnimatingSurfaceTrue() =
+        testScope.runTest {
+            val isAnimatingSurface by collectLastValue(underTest.isAnimatingSurface)
+            transitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.GONE,
+                    to = KeyguardState.LOCKSCREEN,
+                    transitionState = TransitionState.STARTED,
+                )
+            )
+            transitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.GONE,
+                    to = KeyguardState.LOCKSCREEN,
+                    transitionState = TransitionState.FINISHED,
+                )
+            )
+            kosmos.notificationLaunchAnimationInteractor.setIsLaunchAnimationRunning(true)
+            runCurrent()
+            assertThat(isAnimatingSurface).isTrue()
+        }
+
+    @Test
+    fun notificationLaunchFromGone_isAnimatingSurfaceFalse() =
+        testScope.runTest {
+            val isAnimatingSurface by collectLastValue(underTest.isAnimatingSurface)
+            transitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.GONE,
+                    transitionState = TransitionState.STARTED,
+                )
+            )
+            transitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.GONE,
+                    transitionState = TransitionState.FINISHED,
+                )
+            )
+            kosmos.notificationLaunchAnimationInteractor.setIsLaunchAnimationRunning(true)
+            runCurrent()
+            assertThat(isAnimatingSurface).isFalse()
+        }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinderTest.kt
index 66aa572..5e3a142 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinderTest.kt
@@ -113,7 +113,8 @@
                 id = "WEATHER_CLOCK",
                 name = "",
                 description = "",
-                useAlternateSmartspaceAODTransition = true
+                useAlternateSmartspaceAODTransition = true,
+                useCustomClockScene = true
             )
         whenever(clock.config).thenReturn(clockConfig)
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
index d410dac..f1c93c4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
@@ -32,7 +32,6 @@
 import com.google.common.collect.Range
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -67,7 +66,7 @@
     fun transitionToAlternateBouncer_scrimAlphaUpdate() =
         testScope.runTest {
             val scrimAlphas by collectValues(underTest.scrimAlpha)
-            runCurrent()
+            assertThat(scrimAlphas.size).isEqualTo(1) // initial value is 0f
 
             transitionRepository.sendTransitionSteps(
                 listOf(
@@ -79,7 +78,7 @@
                 testScope,
             )
 
-            assertThat(scrimAlphas.size).isEqualTo(4)
+            assertThat(scrimAlphas.size).isEqualTo(5)
             scrimAlphas.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
         }
 
@@ -87,7 +86,7 @@
     fun transitionFromAlternateBouncer_scrimAlphaUpdate() =
         testScope.runTest {
             val scrimAlphas by collectValues(underTest.scrimAlpha)
-            runCurrent()
+            assertThat(scrimAlphas.size).isEqualTo(1) // initial value is 0f
 
             transitionRepository.sendTransitionSteps(
                 listOf(
@@ -98,7 +97,7 @@
                 ),
                 testScope,
             )
-            assertThat(scrimAlphas.size).isEqualTo(4)
+            assertThat(scrimAlphas.size).isEqualTo(5)
             scrimAlphas.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
index 4d20f55..8f73811 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
@@ -50,6 +50,7 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyBoolean
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.ArgumentMatchers.anyLong
 import org.mockito.Mock
@@ -76,9 +77,10 @@
 @TestableLooper.RunWithLooper
 class MediaDataFilterImplTest : SysuiTestCase() {
 
+    @Mock private lateinit var listener: MediaDataProcessor.Listener
     @Mock private lateinit var userTracker: UserTracker
     @Mock private lateinit var broadcastSender: BroadcastSender
-    @Mock private lateinit var mediaDataManager: MediaDataManager
+    @Mock private lateinit var mediaDataProcessor: MediaDataProcessor
     @Mock private lateinit var lockscreenUserManager: NotificationLockscreenUserManager
     @Mock private lateinit var executor: Executor
     @Mock private lateinit var smartspaceData: SmartspaceMediaData
@@ -114,7 +116,8 @@
                 mediaFlags,
                 repository,
             )
-        mediaDataFilter.mediaDataManager = mediaDataManager
+        mediaDataFilter.mediaDataProcessor = mediaDataProcessor
+        mediaDataFilter.addListener(listener)
 
         // Start all tests as main user
         setUser(USER_MAIN)
@@ -167,6 +170,8 @@
 
             mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
 
+            verify(listener)
+                .onMediaDataLoaded(eq(KEY), eq(null), eq(dataMain), eq(true), eq(0), eq(false))
             assertThat(mediaDataLoadedStates).isEqualTo(mediaDataLoadingModel)
         }
 
@@ -178,6 +183,8 @@
 
             mediaDataFilter.onMediaDataLoaded(KEY, null, dataGuest)
 
+            verify(listener, never())
+                .onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyInt(), anyBoolean())
             assertThat(mediaDataLoadedStates).isNotEqualTo(mediaLoadedStatesModel)
         }
 
@@ -196,6 +203,7 @@
             mediaLoadedStatesModel.remove(MediaDataLoadingModel.Loaded(dataMain.instanceId))
             mediaDataFilter.onMediaDataRemoved(KEY)
 
+            verify(listener).onMediaDataRemoved(eq(KEY))
             assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStatesModel)
         }
 
@@ -208,6 +216,7 @@
             mediaDataFilter.onMediaDataLoaded(KEY, null, dataGuest)
             mediaDataFilter.onMediaDataRemoved(KEY)
 
+            verify(listener, never()).onMediaDataRemoved(eq(KEY))
             assertThat(mediaDataLoadedStates).isEmpty()
         }
 
@@ -226,6 +235,7 @@
             setUser(USER_GUEST)
 
             // THEN we should remove the main user's media
+            verify(listener).onMediaDataRemoved(eq(KEY))
             assertThat(mediaDataLoadedStates).isEmpty()
         }
 
@@ -243,6 +253,20 @@
             // and we switch to guest user
             setUser(USER_GUEST)
 
+            // THEN we should add back the guest user media
+            verify(listener)
+                .onMediaDataLoaded(eq(KEY_ALT), eq(null), eq(dataGuest), eq(true), eq(0), eq(false))
+
+            // but not the main user's
+            verify(listener, never())
+                .onMediaDataLoaded(
+                    eq(KEY),
+                    any(),
+                    eq(dataMain),
+                    anyBoolean(),
+                    anyInt(),
+                    anyBoolean()
+                )
             assertThat(mediaDataLoadedStates).isEqualTo(guestLoadedStatesModel)
             assertThat(mediaDataLoadedStates).isNotEqualTo(mainLoadedStatesModel)
         }
@@ -261,6 +285,7 @@
 
             val mediaLoadedStatesModel = listOf(MediaDataLoadingModel.Loaded(dataMain.instanceId))
             // THEN we should remove the private profile media
+            verify(listener).onMediaDataRemoved(eq(KEY_ALT))
             assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStatesModel)
         }
 
@@ -481,7 +506,7 @@
         mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
         mediaDataFilter.onSwipeToDismiss()
 
-        verify(mediaDataManager).setInactive(eq(KEY), eq(true), eq(true))
+        verify(mediaDataProcessor).setInactive(eq(KEY), eq(true), eq(true))
     }
 
     @Test
@@ -507,6 +532,8 @@
                 )
                 .isTrue()
             assertThat(hasActiveMedia(selectedUserEntries)).isFalse()
+            verify(listener)
+                .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(true))
             verify(logger).logRecommendationAdded(SMARTSPACE_PACKAGE, SMARTSPACE_INSTANCE_ID)
             verify(logger, never()).logRecommendationActivated(any(), any(), any())
         }
@@ -534,6 +561,9 @@
                 )
                 .isFalse()
             assertThat(hasActiveMedia(selectedUserEntries)).isFalse()
+            verify(listener, never())
+                .onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyInt(), anyBoolean())
+            verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean())
             verify(logger, never()).logRecommendationAdded(any(), any())
             verify(logger, never()).logRecommendationActivated(any(), any(), any())
         }
@@ -563,6 +593,8 @@
                 )
                 .isTrue()
             assertThat(hasActiveMedia(selectedUserEntries)).isFalse()
+            verify(listener)
+                .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(true))
             verify(logger).logRecommendationAdded(SMARTSPACE_PACKAGE, SMARTSPACE_INSTANCE_ID)
             verify(logger, never()).logRecommendationActivated(any(), any(), any())
         }
@@ -592,6 +624,7 @@
                 )
                 .isFalse()
             assertThat(hasActiveMedia(selectedUserEntries)).isFalse()
+            verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean())
             verify(logger, never()).logRecommendationAdded(any(), any())
             verify(logger, never()).logRecommendationActivated(any(), any(), any())
         }
@@ -614,6 +647,8 @@
             mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
 
             assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStatesModel)
+            verify(listener)
+                .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
 
             // AND we get a smartspace signal
             mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
@@ -629,6 +664,9 @@
                 )
                 .isFalse()
             assertThat(hasActiveMedia(selectedUserEntries)).isFalse()
+            verify(listener, never())
+                .onMediaDataLoaded(eq(KEY), eq(KEY), any(), anyBoolean(), anyInt(), anyBoolean())
+            verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean())
             verify(logger, never()).logRecommendationAdded(any(), any())
             verify(logger, never()).logRecommendationActivated(any(), any(), any())
         }
@@ -649,12 +687,15 @@
             val mediaLoadedStatesModel = listOf(MediaDataLoadingModel.Loaded(dataMain.instanceId))
             mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
             assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStatesModel)
+            verify(listener)
+                .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
 
             // AND we get a smartspace signal
             runCurrent()
             mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
 
             // THEN we should treat the media as active instead
+            val dataCurrentAndActive = dataCurrent.copy(active = true)
             assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStatesModel)
             assertThat(
                     hasActiveMediaOrRecommendation(
@@ -664,8 +705,18 @@
                     )
                 )
                 .isTrue()
+            verify(listener)
+                .onMediaDataLoaded(
+                    eq(KEY),
+                    eq(KEY),
+                    eq(dataCurrentAndActive),
+                    eq(true),
+                    eq(100),
+                    eq(true)
+                )
             // Smartspace update shouldn't be propagated for the empty rec list.
             assertThat(recommendationsLoadingState).isEqualTo(SmartspaceMediaLoadingModel.Unknown)
+            verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean())
             verify(logger, never()).logRecommendationAdded(any(), any())
             verify(logger).logRecommendationActivated(eq(APP_UID), eq(PACKAGE), eq(INSTANCE_ID))
         }
@@ -687,12 +738,24 @@
             mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
 
             assertThat(mediaDataLoadedStates).isEqualTo(mediaDataLoadingModel)
+            verify(listener)
+                .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
 
             // AND we get a smartspace signal
             runCurrent()
             mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
 
             // THEN we should treat the media as active instead
+            val dataCurrentAndActive = dataCurrent.copy(active = true)
+            verify(listener)
+                .onMediaDataLoaded(
+                    eq(KEY),
+                    eq(KEY),
+                    eq(dataCurrentAndActive),
+                    eq(true),
+                    eq(100),
+                    eq(true)
+                )
             assertThat(mediaDataLoadedStates).isEqualTo(mediaDataLoadingModel)
             assertThat(
                     hasActiveMediaOrRecommendation(
@@ -704,6 +767,8 @@
                 .isTrue()
             // Smartspace update should also be propagated but not prioritized.
             assertThat(recommendationsLoadingState).isEqualTo(recommendationsLoadingModel)
+            verify(listener)
+                .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false))
             verify(logger).logRecommendationAdded(SMARTSPACE_PACKAGE, SMARTSPACE_INSTANCE_ID)
             verify(logger).logRecommendationActivated(eq(APP_UID), eq(PACKAGE), eq(INSTANCE_ID))
         }
@@ -721,6 +786,7 @@
             mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
             mediaDataFilter.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
 
+            verify(listener).onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
             assertThat(recommendationsLoadingState).isEqualTo(recommendationsLoadingModel)
             assertThat(
                     hasActiveMediaOrRecommendation(
@@ -746,15 +812,29 @@
             val mediaLoadedStatesModel = listOf(MediaDataLoadingModel.Loaded(dataMain.instanceId))
             val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
             mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
+
             assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStatesModel)
+            verify(listener)
+                .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
 
             runCurrent()
             mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
 
+            val dataCurrentAndActive = dataCurrent.copy(active = true)
+            verify(listener)
+                .onMediaDataLoaded(
+                    eq(KEY),
+                    eq(KEY),
+                    eq(dataCurrentAndActive),
+                    eq(true),
+                    eq(100),
+                    eq(true)
+                )
             assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStatesModel)
 
             mediaDataFilter.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
 
+            verify(listener).onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
             assertThat(recommendationsLoadingState).isEqualTo(recommendationsLoadingModel)
             assertThat(
                     hasActiveMediaOrRecommendation(
@@ -781,6 +861,8 @@
 
             mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
 
+            verify(listener)
+                .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false))
             assertThat(recommendationsLoadingState).isEqualTo(recommendationsLoadingModel)
             assertThat(
                     hasActiveMediaOrRecommendation(
@@ -813,12 +895,18 @@
             val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
             mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
 
+            verify(listener)
+                .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
             assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStatesModel)
 
             // And an inactive recommendation is loaded
             mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
 
             // Smartspace is loaded but the media stays inactive
+            verify(listener)
+                .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false))
+            verify(listener, never())
+                .onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyInt(), anyBoolean())
             assertThat(recommendationsLoadingState).isEqualTo(recommendationsLoadingModel)
             assertThat(
                     hasActiveMediaOrRecommendation(
@@ -846,8 +934,8 @@
         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, data)
         mediaDataFilter.onSwipeToDismiss()
 
-        verify(mediaDataManager).setRecommendationInactive(eq(SMARTSPACE_KEY))
-        verify(mediaDataManager, never())
+        verify(mediaDataProcessor).setRecommendationInactive(eq(SMARTSPACE_KEY))
+        verify(mediaDataProcessor, never())
             .dismissSmartspaceRecommendation(eq(SMARTSPACE_KEY), anyLong())
     }
 
@@ -866,6 +954,8 @@
             val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
             mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
 
+            verify(listener)
+                .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
             assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStatesModel)
 
             // AND we get a smartspace signal with extra to trigger resume
@@ -875,6 +965,16 @@
             mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
 
             // THEN we should treat the media as active instead
+            val dataCurrentAndActive = dataCurrent.copy(active = true)
+            verify(listener)
+                .onMediaDataLoaded(
+                    eq(KEY),
+                    eq(KEY),
+                    eq(dataCurrentAndActive),
+                    eq(true),
+                    eq(100),
+                    eq(true)
+                )
             assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStatesModel)
             assertThat(
                     hasActiveMediaOrRecommendation(
@@ -886,6 +986,8 @@
                 .isTrue()
             // And update the smartspace data state, but not prioritized
             assertThat(recommendationsLoadingState).isEqualTo(recommendationsLoadingModel)
+            verify(listener)
+                .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false))
         }
 
     @Test
@@ -901,6 +1003,8 @@
             val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
             mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
 
+            verify(listener)
+                .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
             assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStatesModel)
 
             // AND we get a smartspace signal with extra to not trigger resume
@@ -908,7 +1012,12 @@
             whenever(cardAction.extras).thenReturn(extras)
             mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
 
+            // THEN listeners are not updated to show media
+            verify(listener, never())
+                .onMediaDataLoaded(eq(KEY), eq(KEY), any(), eq(true), eq(100), eq(true))
             // But the smartspace update is still propagated
+            verify(listener)
+                .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false))
             assertThat(recommendationsLoadingState).isEqualTo(recommendationsLoadingModel)
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt
index a73bb2c..e5d3082 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt
@@ -16,29 +16,54 @@
 
 package com.android.systemui.media.controls.ui.controller
 
+import android.animation.AnimatorSet
+import android.content.Context
 import android.content.res.Configuration
 import android.content.res.Configuration.ORIENTATION_LANDSCAPE
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.GradientDrawable
+import android.graphics.drawable.RippleDrawable
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.View
+import android.view.ViewGroup
+import android.view.animation.Interpolator
+import android.widget.FrameLayout
+import android.widget.ImageButton
+import android.widget.ImageView
+import android.widget.SeekBar
+import android.widget.TextView
 import androidx.constraintlayout.widget.ConstraintSet
+import androidx.lifecycle.LiveData
 import androidx.test.filters.SmallTest
+import com.android.internal.widget.CachingIconView
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.media.controls.ui.view.GutsViewHolder
 import com.android.systemui.media.controls.ui.view.MediaHost
 import com.android.systemui.media.controls.ui.view.MediaViewHolder
 import com.android.systemui.media.controls.ui.view.RecommendationViewHolder
+import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel
 import com.android.systemui.media.controls.util.MediaFlags
 import com.android.systemui.res.R
+import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffectView
+import com.android.systemui.surfaceeffects.ripple.MultiRippleView
+import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseView
 import com.android.systemui.util.animation.MeasurementInput
 import com.android.systemui.util.animation.TransitionLayout
 import com.android.systemui.util.animation.TransitionViewState
 import com.android.systemui.util.animation.WidgetState
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.withArgCaptor
+import com.android.systemui.util.settings.GlobalSettings
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
 import junit.framework.Assert.assertTrue
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.floatThat
 import org.mockito.Mock
+import org.mockito.Mockito
 import org.mockito.Mockito.never
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
@@ -55,6 +80,31 @@
         com.android.systemui.statusbar.phone.ConfigurationControllerImpl(context)
     private var player = TransitionLayout(context, /* attrs */ null, /* defStyleAttr */ 0)
     private var recommendation = TransitionLayout(context, /* attrs */ null, /* defStyleAttr */ 0)
+    private val clock = FakeSystemClock()
+    private lateinit var mainExecutor: FakeExecutor
+    private lateinit var seekBar: SeekBar
+    private lateinit var multiRippleView: MultiRippleView
+    private lateinit var turbulenceNoiseView: TurbulenceNoiseView
+    private lateinit var loadingEffectView: LoadingEffectView
+    private lateinit var settings: ImageButton
+    private lateinit var cancel: View
+    private lateinit var cancelText: TextView
+    private lateinit var dismiss: FrameLayout
+    private lateinit var dismissText: TextView
+    private lateinit var titleText: TextView
+    private lateinit var artistText: TextView
+    private lateinit var explicitIndicator: CachingIconView
+    private lateinit var seamless: ViewGroup
+    private lateinit var seamlessButton: View
+    private lateinit var seamlessIcon: ImageView
+    private lateinit var seamlessText: TextView
+    private lateinit var scrubbingElapsedTimeView: TextView
+    private lateinit var scrubbingTotalTimeView: TextView
+    private lateinit var actionPlayPause: ImageButton
+    private lateinit var actionNext: ImageButton
+    private lateinit var actionPrev: ImageButton
+    @Mock private lateinit var seamlessBackground: RippleDrawable
+    @Mock private lateinit var albumView: ImageView
     @Mock lateinit var logger: MediaViewLogger
     @Mock private lateinit var mockViewState: TransitionViewState
     @Mock private lateinit var mockCopiedState: TransitionViewState
@@ -64,6 +114,14 @@
     @Mock private lateinit var mediaSubTitleWidgetState: WidgetState
     @Mock private lateinit var mediaContainerWidgetState: WidgetState
     @Mock private lateinit var mediaFlags: MediaFlags
+    @Mock private lateinit var seekBarViewModel: SeekBarViewModel
+    @Mock private lateinit var seekBarData: LiveData<SeekBarViewModel.Progress>
+    @Mock private lateinit var globalSettings: GlobalSettings
+    @Mock private lateinit var viewHolder: MediaViewHolder
+    @Mock private lateinit var view: TransitionLayout
+    @Mock private lateinit var mockAnimator: AnimatorSet
+    @Mock private lateinit var gutsViewHolder: GutsViewHolder
+    @Mock private lateinit var gutsText: TextView
 
     private val delta = 0.1F
 
@@ -72,14 +130,30 @@
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
+        mainExecutor = FakeExecutor(clock)
         mediaViewController =
-            MediaViewController(
-                context,
-                configurationController,
-                mediaHostStatesManager,
-                logger,
-                mediaFlags,
-            )
+            object :
+                MediaViewController(
+                    context,
+                    configurationController,
+                    mediaHostStatesManager,
+                    logger,
+                    seekBarViewModel,
+                    mainExecutor,
+                    mediaFlags,
+                    globalSettings,
+                ) {
+                override fun loadAnimator(
+                    context: Context,
+                    animId: Int,
+                    motionInterpolator: Interpolator?,
+                    vararg targets: View?
+                ): AnimatorSet {
+                    return mockAnimator
+                }
+            }
+        initGutsViewHolderMocks()
+        initMediaViewHolderMocks()
     }
 
     @Test
@@ -299,4 +373,270 @@
         verify(mediaTitleWidgetState).alpha = floatThat { kotlin.math.abs(it - 1.0F) < delta }
         verify(mediaSubTitleWidgetState).alpha = floatThat { kotlin.math.abs(it - 1.0F) < delta }
     }
+
+    @Test
+    fun attachPlayer_seekBarDisabled_seekBarVisibilityIsSetToInvisible() {
+        whenever(mediaFlags.isMediaControlsRefactorEnabled()).thenReturn(true)
+
+        mediaViewController.attachPlayer(viewHolder)
+        getEnabledChangeListener().onEnabledChanged(enabled = true)
+        getEnabledChangeListener().onEnabledChanged(enabled = false)
+
+        assertThat(mediaViewController.expandedLayout.getVisibility(R.id.media_progress_bar))
+            .isEqualTo(ConstraintSet.INVISIBLE)
+    }
+
+    @Test
+    fun attachPlayer_seekBarEnabled_seekBarVisible() {
+        whenever(mediaFlags.isMediaControlsRefactorEnabled()).thenReturn(true)
+
+        mediaViewController.attachPlayer(viewHolder)
+        getEnabledChangeListener().onEnabledChanged(enabled = true)
+
+        assertThat(mediaViewController.expandedLayout.getVisibility(R.id.media_progress_bar))
+            .isEqualTo(ConstraintSet.VISIBLE)
+    }
+
+    @Test
+    fun attachPlayer_seekBarStatusUpdate_seekBarVisibilityChanges() {
+        whenever(mediaFlags.isMediaControlsRefactorEnabled()).thenReturn(true)
+
+        mediaViewController.attachPlayer(viewHolder)
+        getEnabledChangeListener().onEnabledChanged(enabled = true)
+
+        assertThat(mediaViewController.expandedLayout.getVisibility(R.id.media_progress_bar))
+            .isEqualTo(ConstraintSet.VISIBLE)
+
+        getEnabledChangeListener().onEnabledChanged(enabled = false)
+
+        assertThat(mediaViewController.expandedLayout.getVisibility(R.id.media_progress_bar))
+            .isEqualTo(ConstraintSet.INVISIBLE)
+    }
+
+    @Test
+    fun attachPlayer_notScrubbing_scrubbingViewsGone() {
+        whenever(mediaFlags.isMediaControlsRefactorEnabled()).thenReturn(true)
+
+        mediaViewController.attachPlayer(viewHolder)
+        mediaViewController.canShowScrubbingTime = true
+        getScrubbingChangeListener().onScrubbingChanged(true)
+        getScrubbingChangeListener().onScrubbingChanged(false)
+        mainExecutor.runAllReady()
+
+        assertThat(
+                mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_elapsed_time)
+            )
+            .isEqualTo(ConstraintSet.GONE)
+        assertThat(
+                mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_total_time)
+            )
+            .isEqualTo(ConstraintSet.GONE)
+    }
+
+    @Test
+    fun setIsScrubbing_noSemanticActions_scrubbingViewsGone() {
+        whenever(mediaFlags.isMediaControlsRefactorEnabled()).thenReturn(true)
+
+        mediaViewController.attachPlayer(viewHolder)
+        mediaViewController.canShowScrubbingTime = false
+        getScrubbingChangeListener().onScrubbingChanged(true)
+        mainExecutor.runAllReady()
+
+        assertThat(
+                mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_elapsed_time)
+            )
+            .isEqualTo(ConstraintSet.GONE)
+        assertThat(
+                mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_total_time)
+            )
+            .isEqualTo(ConstraintSet.GONE)
+    }
+
+    @Test
+    fun setIsScrubbing_noPrevButton_scrubbingTimesNotShown() {
+        whenever(mediaFlags.isMediaControlsRefactorEnabled()).thenReturn(true)
+
+        mediaViewController.attachPlayer(viewHolder)
+        mediaViewController.setUpNextButtonInfo(true)
+        mediaViewController.setUpPrevButtonInfo(false)
+        getScrubbingChangeListener().onScrubbingChanged(true)
+        mainExecutor.runAllReady()
+
+        assertThat(mediaViewController.expandedLayout.getVisibility(R.id.actionNext))
+            .isEqualTo(ConstraintSet.VISIBLE)
+        assertThat(
+                mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_elapsed_time)
+            )
+            .isEqualTo(ConstraintSet.GONE)
+        assertThat(
+                mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_total_time)
+            )
+            .isEqualTo(ConstraintSet.GONE)
+    }
+
+    @Test
+    fun setIsScrubbing_noNextButton_scrubbingTimesNotShown() {
+        whenever(mediaFlags.isMediaControlsRefactorEnabled()).thenReturn(true)
+
+        mediaViewController.attachPlayer(viewHolder)
+        mediaViewController.setUpNextButtonInfo(false)
+        mediaViewController.setUpPrevButtonInfo(true)
+        getScrubbingChangeListener().onScrubbingChanged(true)
+        mainExecutor.runAllReady()
+
+        assertThat(mediaViewController.expandedLayout.getVisibility(R.id.actionPrev))
+            .isEqualTo(ConstraintSet.VISIBLE)
+        assertThat(
+                mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_elapsed_time)
+            )
+            .isEqualTo(ConstraintSet.GONE)
+        assertThat(
+                mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_total_time)
+            )
+            .isEqualTo(ConstraintSet.GONE)
+    }
+
+    @Test
+    fun setIsScrubbing_scrubbingViewsShownAndPrevNextHiddenOnlyInExpanded() {
+        whenever(mediaFlags.isMediaControlsRefactorEnabled()).thenReturn(true)
+
+        mediaViewController.attachPlayer(viewHolder)
+        mediaViewController.setUpNextButtonInfo(true)
+        mediaViewController.setUpPrevButtonInfo(true)
+        mediaViewController.canShowScrubbingTime = true
+        getScrubbingChangeListener().onScrubbingChanged(true)
+        mainExecutor.runAllReady()
+
+        // Only in expanded, we should show the scrubbing times and hide prev+next
+        assertThat(mediaViewController.expandedLayout.getVisibility(R.id.actionPrev))
+            .isEqualTo(ConstraintSet.GONE)
+        assertThat(mediaViewController.expandedLayout.getVisibility(R.id.actionNext))
+            .isEqualTo(ConstraintSet.GONE)
+        assertThat(
+                mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_elapsed_time)
+            )
+            .isEqualTo(ConstraintSet.VISIBLE)
+        assertThat(
+                mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_total_time)
+            )
+            .isEqualTo(ConstraintSet.VISIBLE)
+    }
+
+    @Test
+    fun setIsScrubbing_trueThenFalse_reservePrevAndNextButtons() {
+        whenever(mediaFlags.isMediaControlsRefactorEnabled()).thenReturn(true)
+
+        mediaViewController.attachPlayer(viewHolder)
+        mediaViewController.setUpNextButtonInfo(true, ConstraintSet.INVISIBLE)
+        mediaViewController.setUpPrevButtonInfo(true, ConstraintSet.INVISIBLE)
+        mediaViewController.canShowScrubbingTime = true
+
+        getScrubbingChangeListener().onScrubbingChanged(true)
+        mainExecutor.runAllReady()
+
+        assertThat(mediaViewController.expandedLayout.getVisibility(R.id.actionPrev))
+            .isEqualTo(ConstraintSet.INVISIBLE)
+        assertThat(mediaViewController.expandedLayout.getVisibility(R.id.actionNext))
+            .isEqualTo(ConstraintSet.INVISIBLE)
+        assertThat(
+                mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_elapsed_time)
+            )
+            .isEqualTo(ConstraintSet.VISIBLE)
+        assertThat(
+                mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_total_time)
+            )
+            .isEqualTo(ConstraintSet.VISIBLE)
+
+        getScrubbingChangeListener().onScrubbingChanged(false)
+        mainExecutor.runAllReady()
+
+        // Only in expanded, we should hide the scrubbing times and show prev+next
+        assertThat(mediaViewController.expandedLayout.getVisibility(R.id.actionPrev))
+            .isEqualTo(ConstraintSet.VISIBLE)
+        assertThat(mediaViewController.expandedLayout.getVisibility(R.id.actionNext))
+            .isEqualTo(ConstraintSet.VISIBLE)
+        assertThat(
+                mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_elapsed_time)
+            )
+            .isEqualTo(ConstraintSet.GONE)
+        assertThat(
+                mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_total_time)
+            )
+            .isEqualTo(ConstraintSet.GONE)
+    }
+
+    private fun initGutsViewHolderMocks() {
+        settings = ImageButton(context)
+        cancel = View(context)
+        cancelText = TextView(context)
+        dismiss = FrameLayout(context)
+        dismissText = TextView(context)
+        whenever(gutsViewHolder.gutsText).thenReturn(gutsText)
+        whenever(gutsViewHolder.settings).thenReturn(settings)
+        whenever(gutsViewHolder.cancel).thenReturn(cancel)
+        whenever(gutsViewHolder.cancelText).thenReturn(cancelText)
+        whenever(gutsViewHolder.dismiss).thenReturn(dismiss)
+        whenever(gutsViewHolder.dismissText).thenReturn(dismissText)
+    }
+
+    private fun initMediaViewHolderMocks() {
+        titleText = TextView(context)
+        artistText = TextView(context)
+        explicitIndicator = CachingIconView(context).also { it.id = R.id.media_explicit_indicator }
+        seamless = FrameLayout(context)
+        seamless.foreground = seamlessBackground
+        seamlessButton = View(context)
+        seamlessIcon = ImageView(context)
+        seamlessText = TextView(context)
+        seekBar = SeekBar(context).also { it.id = R.id.media_progress_bar }
+
+        actionPlayPause = ImageButton(context).also { it.id = R.id.actionPlayPause }
+        actionPrev = ImageButton(context).also { it.id = R.id.actionPrev }
+        actionNext = ImageButton(context).also { it.id = R.id.actionNext }
+        scrubbingElapsedTimeView =
+            TextView(context).also { it.id = R.id.media_scrubbing_elapsed_time }
+        scrubbingTotalTimeView = TextView(context).also { it.id = R.id.media_scrubbing_total_time }
+
+        multiRippleView = MultiRippleView(context, null)
+        turbulenceNoiseView = TurbulenceNoiseView(context, null)
+        loadingEffectView = LoadingEffectView(context, null)
+
+        whenever(viewHolder.player).thenReturn(view)
+        whenever(view.context).thenReturn(context)
+        whenever(viewHolder.albumView).thenReturn(albumView)
+        whenever(albumView.foreground).thenReturn(Mockito.mock(Drawable::class.java))
+        whenever(viewHolder.titleText).thenReturn(titleText)
+        whenever(viewHolder.artistText).thenReturn(artistText)
+        whenever(viewHolder.explicitIndicator).thenReturn(explicitIndicator)
+        whenever(seamlessBackground.getDrawable(0))
+            .thenReturn(Mockito.mock(GradientDrawable::class.java))
+        whenever(viewHolder.seamless).thenReturn(seamless)
+        whenever(viewHolder.seamlessButton).thenReturn(seamlessButton)
+        whenever(viewHolder.seamlessIcon).thenReturn(seamlessIcon)
+        whenever(viewHolder.seamlessText).thenReturn(seamlessText)
+        whenever(viewHolder.seekBar).thenReturn(seekBar)
+        whenever(viewHolder.scrubbingElapsedTimeView).thenReturn(scrubbingElapsedTimeView)
+        whenever(viewHolder.scrubbingTotalTimeView).thenReturn(scrubbingTotalTimeView)
+        whenever(viewHolder.gutsViewHolder).thenReturn(gutsViewHolder)
+        whenever(seekBarViewModel.progress).thenReturn(seekBarData)
+
+        // Action buttons
+        whenever(viewHolder.actionPlayPause).thenReturn(actionPlayPause)
+        whenever(viewHolder.getAction(R.id.actionNext)).thenReturn(actionNext)
+        whenever(viewHolder.getAction(R.id.actionPrev)).thenReturn(actionPrev)
+        whenever(viewHolder.getAction(R.id.actionPlayPause)).thenReturn(actionPlayPause)
+
+        whenever(viewHolder.multiRippleView).thenReturn(multiRippleView)
+        whenever(viewHolder.turbulenceNoiseView).thenReturn(turbulenceNoiseView)
+        whenever(viewHolder.loadingEffectView).thenReturn(loadingEffectView)
+    }
+
+    private fun getScrubbingChangeListener(): SeekBarViewModel.ScrubbingChangeListener =
+        withArgCaptor {
+            verify(seekBarViewModel).setScrubbingChangeListener(capture())
+        }
+
+    private fun getEnabledChangeListener(): SeekBarViewModel.EnabledChangeListener = withArgCaptor {
+        verify(seekBarViewModel).setEnabledChangeListener(capture())
+    }
 }
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 b04503b..8c5a4d0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -578,6 +578,14 @@
         assertEquals(keyEvent, falsingCollector.lastKeyEvent)
     }
 
+    @Test
+    fun cancelCurrentTouch_callsDragDownHelper() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
+        underTest.cancelCurrentTouch()
+
+        verify(dragDownHelper).stopDragging()
+    }
+
     companion object {
         private val DOWN_EVENT = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
         private val MOVE_EVENT = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shadow/DoubleShadowTextClockTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shadow/DoubleShadowTextClockTest.kt
index eb418fd..4679a58 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shadow/DoubleShadowTextClockTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shadow/DoubleShadowTextClockTest.kt
@@ -19,11 +19,12 @@
 import android.content.Context
 import android.content.res.Resources
 import android.content.res.TypedArray
+import android.platform.test.annotations.PlatinumTest
 import android.testing.AndroidTestingRunner
 import android.util.AttributeSet
 import androidx.test.filters.SmallTest
-import com.android.systemui.shared.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.shared.R
 import com.android.systemui.shared.shadow.DoubleShadowTextClock
 import com.android.systemui.util.mockito.whenever
 import junit.framework.Assert.assertTrue
@@ -36,6 +37,7 @@
 import org.mockito.junit.MockitoJUnit
 import org.mockito.junit.MockitoRule
 
+@PlatinumTest(focusArea = "sysui")
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 class DoubleShadowTextClockTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt
index 56fc0c7..a05a23b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt
@@ -21,7 +21,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction
 import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.ViewIdToTranslate
-import com.android.systemui.unfold.TestUnfoldTransitionProvider
+import com.android.systemui.unfold.FakeUnfoldTransitionProvider
 import org.junit.Assert.assertEquals
 import org.junit.Before
 import org.junit.Test
@@ -34,21 +34,19 @@
 @RunWith(AndroidTestingRunner::class)
 class UnfoldConstantTranslateAnimatorTest : SysuiTestCase() {
 
-    private val progressProvider = TestUnfoldTransitionProvider()
+    private val progressProvider = FakeUnfoldTransitionProvider()
 
-    @Mock
-    private lateinit var parent: ViewGroup
+    @Mock private lateinit var parent: ViewGroup
 
-    @Mock
-    private lateinit var shouldBeAnimated: () -> Boolean
+    @Mock private lateinit var shouldBeAnimated: () -> Boolean
 
     private lateinit var animator: UnfoldConstantTranslateAnimator
 
     private val viewsIdToRegister
         get() =
             setOf(
-                    ViewIdToTranslate(START_VIEW_ID, Direction.START, shouldBeAnimated),
-                    ViewIdToTranslate(END_VIEW_ID, Direction.END, shouldBeAnimated)
+                ViewIdToTranslate(START_VIEW_ID, Direction.START, shouldBeAnimated),
+                ViewIdToTranslate(END_VIEW_ID, Direction.END, shouldBeAnimated)
             )
 
     @Before
@@ -122,11 +120,12 @@
         progressProvider.onTransitionStarted()
         progressProvider.onTransitionProgress(0f)
 
-        val rtlMultiplier = if (layoutDirection == View.LAYOUT_DIRECTION_LTR) {
-            1
-        } else {
-            -1
-        }
+        val rtlMultiplier =
+            if (layoutDirection == View.LAYOUT_DIRECTION_LTR) {
+                1
+            } else {
+                -1
+            }
         list.forEach { (view, direction) ->
             assertEquals(
                 (-MAX_TRANSLATION * direction * rtlMultiplier).toInt(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java
index eb692eb..0e24ed4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java
@@ -40,6 +40,9 @@
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.RowContentBindParams;
 import com.android.systemui.statusbar.notification.row.RowContentBindStage;
+import com.android.systemui.statusbar.notification.row.RowInflaterTask;
+import com.android.systemui.statusbar.notification.row.RowInflaterTaskLogger;
+import com.android.systemui.util.time.FakeSystemClock;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -114,20 +117,25 @@
 
     private NotificationEntry addGroup(int size) {
         NotificationEntry summary = new NotificationEntryBuilder().build();
-        summary.setRow(createRow());
+        summary.setRow(createRow(summary));
         ArrayList<NotificationEntry> children = new ArrayList<>();
         for (int i = 0; i < size; i++) {
             NotificationEntry child = new NotificationEntryBuilder().build();
-            child.setRow(createRow());
+            child.setRow(createRow(child));
             children.add(child);
         }
         mGroupNotifs.put(summary, children);
         return summary;
     }
 
-    private ExpandableNotificationRow createRow() {
+    private ExpandableNotificationRow createRow(NotificationEntry entry) {
+        LayoutInflater inflater = LayoutInflater.from(mContext);
+        inflater.setFactory2(
+                new RowInflaterTask.RowAsyncLayoutInflater(entry, new FakeSystemClock(), mock(
+                        RowInflaterTaskLogger.class)));
+
         ExpandableNotificationRow row = (ExpandableNotificationRow)
-                LayoutInflater.from(mContext).inflate(R.layout.status_bar_notification_row, null);
+                inflater.inflate(R.layout.status_bar_notification_row, null);
         row.getPrivateLayout().setContractedChild(new View(mContext));
         row.getPrivateLayout().setExpandedChild(new View(mContext));
         return row;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index 01492f6..aa79c23 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -772,8 +772,7 @@
         row.setUserExpanded(true);
         row.setOnKeyguard(false);
         row.setSensitive(/* sensitive= */true, /* hideSensitive= */false);
-        row.setHideSensitive(/* hideSensitive= */true, /* animated= */false,
-                /* delay= */0L, /* duration= */0L);
+        row.setHideSensitiveForIntrinsicHeight(/* hideSensitive= */true);
 
         // THEN
         assertThat(row.isExpanded()).isFalse();
@@ -787,8 +786,7 @@
         row.setUserExpanded(true);
         row.setOnKeyguard(false);
         row.setSensitive(/* sensitive= */true, /* hideSensitive= */false);
-        row.setHideSensitive(/* hideSensitive= */false, /* animated= */false,
-                /* delay= */0L, /* duration= */0L);
+        row.setHideSensitiveForIntrinsicHeight(/* hideSensitive= */false);
 
         // THEN
         assertThat(row.isExpanded()).isTrue();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt
index 91e4666..7332bc3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt
@@ -79,10 +79,11 @@
         initMocks(this)
         fakeParent =
             spy(FrameLayout(mContext, /* attrs= */ null).also { it.visibility = View.GONE })
+        val mockEntry = createMockNotificationEntry()
         row =
             spy(
-                ExpandableNotificationRow(mContext, /* attrs= */ null).apply {
-                    entry = createMockNotificationEntry()
+                ExpandableNotificationRow(mContext, /* attrs= */ null, mockEntry).apply {
+                    entry = mockEntry
                 }
             )
         ViewUtils.attachView(fakeParent)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
index 9a7b8ec..745d20d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
@@ -1,5 +1,6 @@
 package com.android.systemui.statusbar.notification.stack
 
+import android.service.notification.StatusBarNotification
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper.RunWithLooper
 import android.view.LayoutInflater
@@ -14,6 +15,7 @@
 import com.android.systemui.shade.transition.LargeScreenShadeInterpolator
 import com.android.systemui.statusbar.NotificationShelf
 import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.row.ExpandableView
 import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
@@ -457,8 +459,13 @@
         expansionFraction: Float,
         expectedAlpha: Float
     ) {
+        val sbnMock: StatusBarNotification = mock()
+        val mockEntry = mock<NotificationEntry>().apply {
+            whenever(this.sbn).thenReturn(sbnMock)
+        }
+        val row = ExpandableNotificationRow(mContext, null, mockEntry)
         whenever(ambientState.lastVisibleBackgroundChild)
-            .thenReturn(ExpandableNotificationRow(mContext, null))
+            .thenReturn(row)
         whenever(ambientState.isExpansionChanging).thenReturn(true)
         whenever(ambientState.expansionFraction).thenReturn(expansionFraction)
         whenever(hostLayoutController.speedBumpIndex).thenReturn(0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt
index 4bfd7e3..e2ac203 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt
@@ -31,9 +31,9 @@
 import com.android.systemui.power.shared.model.ScreenPowerState.SCREEN_ON
 import com.android.systemui.power.shared.model.WakefulnessState.STARTING_TO_SLEEP
 import com.android.systemui.statusbar.policy.FakeConfigurationController
-import com.android.systemui.unfold.TestUnfoldTransitionProvider
+import com.android.systemui.unfold.FakeUnfoldTransitionProvider
 import com.android.systemui.unfold.data.repository.UnfoldTransitionRepositoryImpl
-import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractorImpl
+import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
 import com.android.systemui.util.animation.data.repository.FakeAnimationStatusRepository
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
@@ -59,7 +59,7 @@
 
     private val animationStatus = FakeAnimationStatusRepository()
     private val configurationController = FakeConfigurationController()
-    private val unfoldTransitionProgressProvider = TestUnfoldTransitionProvider()
+    private val unfoldTransitionProgressProvider = FakeUnfoldTransitionProvider()
     private val powerRepository = FakePowerRepository()
     private val powerInteractor =
         PowerInteractor(
@@ -69,11 +69,6 @@
             statusBarStateController = mock()
         )
 
-    private val unfoldTransitionRepository =
-        UnfoldTransitionRepositoryImpl(Optional.of(unfoldTransitionProgressProvider))
-    private val unfoldTransitionInteractor =
-        UnfoldTransitionInteractorImpl(unfoldTransitionRepository)
-
     private val configurationRepository =
         ConfigurationRepositoryImpl(
             configurationController,
@@ -83,6 +78,11 @@
         )
     private val configurationInteractor = ConfigurationInteractor(configurationRepository)
 
+    private val unfoldTransitionRepository =
+        UnfoldTransitionRepositoryImpl(Optional.of(unfoldTransitionProgressProvider))
+    private val unfoldTransitionInteractor =
+        UnfoldTransitionInteractor(unfoldTransitionRepository, configurationInteractor)
+
     private lateinit var configuration: Configuration
     private lateinit var underTest: HideNotificationsInteractor
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt
index feff046..1ec1765 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt
@@ -8,7 +8,7 @@
 import android.view.WindowManager
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.unfold.TestUnfoldTransitionProvider
+import com.android.systemui.unfold.FakeUnfoldTransitionProvider
 import com.android.systemui.unfold.util.CurrentActivityTypeProvider
 import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
 import com.android.systemui.util.mockito.whenever
@@ -23,17 +23,14 @@
 @TestableLooper.RunWithLooper
 class StatusBarMoveFromCenterAnimationControllerTest : SysuiTestCase() {
 
-    @Mock
-    private lateinit var windowManager: WindowManager
+    @Mock private lateinit var windowManager: WindowManager
 
-    @Mock
-    private lateinit var display: Display
+    @Mock private lateinit var display: Display
 
-    @Mock
-    private lateinit var currentActivityTypeProvider: CurrentActivityTypeProvider
+    @Mock private lateinit var currentActivityTypeProvider: CurrentActivityTypeProvider
 
     private val view: View = View(context)
-    private val progressProvider = TestUnfoldTransitionProvider()
+    private val progressProvider = FakeUnfoldTransitionProvider()
     private val scopedProvider = ScopedUnfoldTransitionProgressProvider(progressProvider)
 
     private lateinit var controller: StatusBarMoveFromCenterAnimationController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
index 42bbe3e..c4ab943 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
@@ -26,6 +26,10 @@
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
 import com.android.systemui.statusbar.pipeline.satellite.data.prod.FakeDeviceBasedSatelliteRepository
 import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState
+import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
 import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository
 import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor
 import com.android.systemui.util.mockito.mock
@@ -53,6 +57,10 @@
     private val deviceProvisionedRepository = FakeDeviceProvisioningRepository()
     private val deviceProvisioningInteractor =
         DeviceProvisioningInteractor(deviceProvisionedRepository)
+    private val connectivityRepository = FakeConnectivityRepository()
+    private val wifiRepository = FakeWifiRepository()
+    private val wifiInteractor =
+        WifiInteractorImpl(connectivityRepository, wifiRepository, testScope.backgroundScope)
 
     @Before
     fun setUp() {
@@ -61,6 +69,7 @@
                 repo,
                 iconsInteractor,
                 deviceProvisioningInteractor,
+                wifiInteractor,
                 testScope.backgroundScope,
             )
     }
@@ -103,6 +112,7 @@
                     repo,
                     iconsInteractor,
                     deviceProvisioningInteractor,
+                    wifiInteractor,
                     testScope.backgroundScope,
                 )
 
@@ -150,6 +160,7 @@
                     repo,
                     iconsInteractor,
                     deviceProvisioningInteractor,
+                    wifiInteractor,
                     testScope.backgroundScope,
                 )
 
@@ -205,6 +216,7 @@
                     repo,
                     iconsInteractor,
                     deviceProvisioningInteractor,
+                    wifiInteractor,
                     testScope.backgroundScope,
                 )
 
@@ -337,6 +349,7 @@
                     repo,
                     iconsInteractor,
                     deviceProvisioningInteractor,
+                    wifiInteractor,
                     testScope.backgroundScope,
                 )
 
@@ -353,4 +366,28 @@
             // THEN the value is still false, because the flag is off
             assertThat(latest).isFalse()
         }
+
+    @Test
+    fun isWifiActive_falseWhenWifiNotActive() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.isWifiActive)
+
+            // WHEN wifi is not active
+            wifiRepository.setWifiNetwork(WifiNetworkModel.Invalid("test"))
+
+            // THEN the interactor returns false due to the wifi network not being active
+            assertThat(latest).isFalse()
+        }
+
+    @Test
+    fun isWifiActive_trueWhenWifiIsActive() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.isWifiActive)
+
+            // WHEN wifi is active
+            wifiRepository.setWifiNetwork(WifiNetworkModel.Active(networkId = 0, level = 1))
+
+            // THEN the interactor returns true due to the wifi network being active
+            assertThat(latest).isTrue()
+        }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
index 1d6cd37..64f19b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
@@ -26,6 +26,10 @@
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
 import com.android.systemui.statusbar.pipeline.satellite.data.prod.FakeDeviceBasedSatelliteRepository
 import com.android.systemui.statusbar.pipeline.satellite.domain.interactor.DeviceBasedSatelliteInteractor
+import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
 import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository
 import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor
 import com.android.systemui.util.mockito.mock
@@ -44,14 +48,18 @@
     private lateinit var underTest: DeviceBasedSatelliteViewModel
     private lateinit var interactor: DeviceBasedSatelliteInteractor
     private lateinit var airplaneModeRepository: FakeAirplaneModeRepository
-
     private val repo = FakeDeviceBasedSatelliteRepository()
+    private val testScope = TestScope()
+
     private val mobileIconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
+
     private val deviceProvisionedRepository = FakeDeviceProvisioningRepository()
     private val deviceProvisioningInteractor =
         DeviceProvisioningInteractor(deviceProvisionedRepository)
-
-    private val testScope = TestScope()
+    private val connectivityRepository = FakeConnectivityRepository()
+    private val wifiRepository = FakeWifiRepository()
+    private val wifiInteractor =
+        WifiInteractorImpl(connectivityRepository, wifiRepository, testScope.backgroundScope)
 
     @Before
     fun setUp() {
@@ -63,6 +71,7 @@
                 repo,
                 mobileIconsInteractor,
                 deviceProvisioningInteractor,
+                wifiInteractor,
                 testScope.backgroundScope,
             )
 
@@ -253,4 +262,40 @@
             // THEN icon is null because the device is not provisioned
             assertThat(latest).isInstanceOf(Icon::class.java)
         }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun icon_wifiIsActive() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.icon)
+
+            // GIVEN satellite is allowed
+            repo.isSatelliteAllowedForCurrentLocation.value = true
+
+            // GIVEN all icons are OOS
+            val i1 = mobileIconsInteractor.getMobileConnectionInteractorForSubId(1)
+            i1.isInService.value = false
+            i1.isEmergencyOnly.value = false
+
+            // GIVEN apm is disabled
+            airplaneModeRepository.setIsAirplaneMode(false)
+
+            // GIVEN device is provisioned
+            deviceProvisionedRepository.setDeviceProvisioned(true)
+
+            // GIVEN wifi network is active
+            wifiRepository.setWifiNetwork(WifiNetworkModel.Active(networkId = 0, level = 1))
+
+            // THEN icon is null because the device is connected to wifi
+            assertThat(latest).isNull()
+
+            // GIVEN device loses wifi connection
+            wifiRepository.setWifiNetwork(WifiNetworkModel.Invalid("test"))
+
+            // Wait for delay to be completed
+            advanceTimeBy(10.seconds)
+
+            // THEN icon is set because the device lost wifi connection
+            assertThat(latest).isInstanceOf(Icon::class.java)
+        }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
index 28adbce..2cdc8d8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
@@ -22,6 +22,8 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.ui.data.repository.ConfigurationRepositoryImpl
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
 import com.android.systemui.display.data.repository.DeviceStateRepository
 import com.android.systemui.display.data.repository.DeviceStateRepository.DeviceState
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -31,11 +33,12 @@
 import com.android.systemui.power.shared.model.WakefulnessModel
 import com.android.systemui.power.shared.model.WakefulnessState
 import com.android.systemui.shared.system.SysUiStatsLog
+import com.android.systemui.statusbar.policy.FakeConfigurationController
 import com.android.systemui.unfold.DisplaySwitchLatencyTracker.Companion.FOLDABLE_DEVICE_STATE_CLOSED
 import com.android.systemui.unfold.DisplaySwitchLatencyTracker.Companion.FOLDABLE_DEVICE_STATE_HALF_OPEN
 import com.android.systemui.unfold.DisplaySwitchLatencyTracker.DisplaySwitchLatencyEvent
 import com.android.systemui.unfold.data.repository.UnfoldTransitionRepositoryImpl
-import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractorImpl
+import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
 import com.android.systemui.util.animation.data.repository.AnimationStatusRepository
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.capture
@@ -86,11 +89,20 @@
     private val areAnimationEnabled = MutableStateFlow(true)
     private val lastWakefulnessEvent = MutableStateFlow(WakefulnessModel())
     private val systemClock = FakeSystemClock()
-    private val unfoldTransitionProgressProvider = TestUnfoldTransitionProvider()
+    private val configurationController = FakeConfigurationController()
+    private val configurationRepository =
+        ConfigurationRepositoryImpl(
+            configurationController,
+            context,
+            testScope.backgroundScope,
+            mock()
+        )
+    private val configurationInteractor = ConfigurationInteractor(configurationRepository)
+    private val unfoldTransitionProgressProvider = FakeUnfoldTransitionProvider()
     private val unfoldTransitionRepository =
         UnfoldTransitionRepositoryImpl(Optional.of(unfoldTransitionProgressProvider))
     private val unfoldTransitionInteractor =
-        UnfoldTransitionInteractorImpl(unfoldTransitionRepository)
+        UnfoldTransitionInteractor(unfoldTransitionRepository, configurationInteractor)
 
     @Before
     fun setup() {
@@ -155,7 +167,10 @@
     fun unfold_progressUnavailable_logsLatencyTillScreenTurnedOn() {
         testScope.runTest {
             val unfoldTransitionInteractorWithEmptyProgressProvider =
-                UnfoldTransitionInteractorImpl(UnfoldTransitionRepositoryImpl(Optional.empty()))
+                UnfoldTransitionInteractor(
+                    UnfoldTransitionRepositoryImpl(Optional.empty()),
+                    configurationInteractor,
+                )
             displaySwitchLatencyTracker =
                 DisplaySwitchLatencyTracker(
                     mockContext,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt
index b9c7e61..fd513c9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt
@@ -35,7 +35,7 @@
 @SmallTest
 class UnfoldHapticsPlayerTest : SysuiTestCase() {
 
-    private val progressProvider = TestUnfoldTransitionProvider()
+    private val progressProvider = FakeUnfoldTransitionProvider()
     private val vibrator: Vibrator = mock()
     private val testFoldProvider = TestFoldProvider()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
index ba72716..2955384 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
@@ -27,6 +27,7 @@
 import com.android.systemui.unfold.util.FoldableDeviceStates
 import com.android.systemui.unfold.util.FoldableTestUtils
 import com.android.systemui.util.mockito.any
+import java.util.Optional
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -37,45 +38,41 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyNoMoreInteractions
 import org.mockito.MockitoAnnotations
-import java.util.Optional
 
 @RunWith(AndroidTestingRunner::class)
 @SmallTest
 class UnfoldLatencyTrackerTest : SysuiTestCase() {
 
-    @Mock
-    lateinit var latencyTracker: LatencyTracker
+    @Mock lateinit var latencyTracker: LatencyTracker
 
-    @Mock
-    lateinit var deviceStateManager: DeviceStateManager
+    @Mock lateinit var deviceStateManager: DeviceStateManager
 
-    @Mock
-    lateinit var screenLifecycle: ScreenLifecycle
+    @Mock lateinit var screenLifecycle: ScreenLifecycle
 
-    @Captor
-    private lateinit var foldStateListenerCaptor: ArgumentCaptor<FoldStateListener>
+    @Captor private lateinit var foldStateListenerCaptor: ArgumentCaptor<FoldStateListener>
 
-    @Captor
-    private lateinit var screenLifecycleCaptor: ArgumentCaptor<ScreenLifecycle.Observer>
+    @Captor private lateinit var screenLifecycleCaptor: ArgumentCaptor<ScreenLifecycle.Observer>
 
     private lateinit var deviceStates: FoldableDeviceStates
 
     private lateinit var unfoldLatencyTracker: UnfoldLatencyTracker
 
-    private val transitionProgressProvider = TestUnfoldTransitionProvider()
+    private val transitionProgressProvider = FakeUnfoldTransitionProvider()
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-        unfoldLatencyTracker = UnfoldLatencyTracker(
-            latencyTracker,
-            deviceStateManager,
-            Optional.of(transitionProgressProvider),
-            context.mainExecutor,
-            context,
-            context.contentResolver,
-            screenLifecycle
-        ).apply { init() }
+        unfoldLatencyTracker =
+            UnfoldLatencyTracker(
+                    latencyTracker,
+                    deviceStateManager,
+                    Optional.of(transitionProgressProvider),
+                    context.mainExecutor,
+                    context,
+                    context.contentResolver,
+                    screenLifecycle
+                )
+                .apply { init() }
         deviceStates = FoldableTestUtils.findDeviceStates(context)
 
         verify(deviceStateManager).registerCallback(any(), foldStateListenerCaptor.capture())
@@ -107,7 +104,7 @@
     }
 
     @Test
-    fun unfold_firstFoldEventAnimationsEnabledOnScreenTurnedOnAndTransitionStarted_eventNotPropagated() {
+    fun firstFoldEventAnimationsEnabledOnScreenTurnedOnAndTransitionStarted_eventNotPropagated() {
         setAnimationsEnabled(true)
         sendFoldEvent(folded = false)
 
@@ -118,7 +115,7 @@
     }
 
     @Test
-    fun unfold_secondFoldEventAnimationsEnabledOnScreenTurnedOnAndTransitionStarted_eventPropagated() {
+    fun secondFoldEventAnimationsEnabledOnScreenTurnedOnAndTransitionStarted_eventPropagated() {
         setAnimationsEnabled(true)
         sendFoldEvent(folded = true)
         sendFoldEvent(folded = false)
@@ -131,7 +128,7 @@
     }
 
     @Test
-    fun unfold_unfoldFoldUnfoldAnimationsEnabledOnScreenTurnedOnAndTransitionStarted_eventPropagated() {
+    fun unfoldFoldUnfoldAnimationsEnabledOnScreenTurnedOnAndTransitionStarted_eventPropagated() {
         setAnimationsEnabled(true)
         sendFoldEvent(folded = false)
         sendFoldEvent(folded = true)
@@ -196,4 +193,4 @@
             durationScale.toString()
         )
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldTransitionWallpaperControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldTransitionWallpaperControllerTest.kt
index 6ec0251..0c452eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldTransitionWallpaperControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldTransitionWallpaperControllerTest.kt
@@ -17,21 +17,18 @@
 @SmallTest
 class UnfoldTransitionWallpaperControllerTest : SysuiTestCase() {
 
-    @Mock
-    private lateinit var wallpaperController: WallpaperController
+    @Mock private lateinit var wallpaperController: WallpaperController
 
-    private val progressProvider = TestUnfoldTransitionProvider()
+    private val progressProvider = FakeUnfoldTransitionProvider()
 
-    @JvmField
-    @Rule
-    val mockitoRule = MockitoJUnit.rule()
+    @JvmField @Rule val mockitoRule = MockitoJUnit.rule()
 
     private lateinit var unfoldWallpaperController: UnfoldTransitionWallpaperController
 
     @Before
     fun setup() {
-        unfoldWallpaperController = UnfoldTransitionWallpaperController(progressProvider,
-            wallpaperController)
+        unfoldWallpaperController =
+            UnfoldTransitionWallpaperController(progressProvider, wallpaperController)
         unfoldWallpaperController.init()
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractorTest.kt
deleted file mode 100644
index 6a801e0..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractorTest.kt
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-package com.android.systemui.unfold.domain.interactor
-
-import android.testing.AndroidTestingRunner
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.unfold.TestUnfoldTransitionProvider
-import com.android.systemui.unfold.data.repository.UnfoldTransitionRepositoryImpl
-import com.google.common.truth.Truth.assertThat
-import java.util.Optional
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.async
-import kotlinx.coroutines.test.TestScope
-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.MockitoAnnotations
-
-@OptIn(ExperimentalCoroutinesApi::class)
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-open class UnfoldTransitionInteractorTest : SysuiTestCase() {
-
-    private val testScope = TestScope()
-
-    private val unfoldTransitionProgressProvider = TestUnfoldTransitionProvider()
-    private val unfoldTransitionRepository =
-        UnfoldTransitionRepositoryImpl(Optional.of(unfoldTransitionProgressProvider))
-
-    private lateinit var underTest: UnfoldTransitionInteractor
-
-    @Before
-    fun setUp() {
-        MockitoAnnotations.initMocks(this)
-
-        underTest = UnfoldTransitionInteractorImpl(unfoldTransitionRepository)
-    }
-
-    @Test
-    fun waitForTransitionFinish_noEvents_doesNotComplete() =
-        testScope.runTest {
-            val deferred = async { underTest.waitForTransitionFinish() }
-
-            runCurrent()
-
-            assertThat(deferred.isCompleted).isFalse()
-            deferred.cancel()
-        }
-
-    @Test
-    fun waitForTransitionFinish_finishEvent_completes() =
-        testScope.runTest {
-            val deferred = async { underTest.waitForTransitionFinish() }
-
-            runCurrent()
-            unfoldTransitionProgressProvider.onTransitionFinished()
-            runCurrent()
-
-            assertThat(deferred.isCompleted).isTrue()
-            deferred.cancel()
-        }
-
-    @Test
-    fun waitForTransitionFinish_otherEvent_doesNotComplete() =
-        testScope.runTest {
-            val deferred = async { underTest.waitForTransitionFinish() }
-
-            runCurrent()
-            unfoldTransitionProgressProvider.onTransitionStarted()
-            runCurrent()
-
-            assertThat(deferred.isCompleted).isFalse()
-            deferred.cancel()
-        }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt
index 2bc05fc..e5f619b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt
@@ -23,7 +23,7 @@
 import android.testing.TestableLooper.RunWithLooper
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.unfold.TestUnfoldTransitionProvider
+import com.android.systemui.unfold.FakeUnfoldTransitionProvider
 import com.android.systemui.utils.os.FakeHandler
 import kotlin.test.Test
 import kotlinx.coroutines.test.runTest
@@ -34,7 +34,7 @@
 @RunWithLooper(setAsMainLooper = true)
 class MainThreadUnfoldTransitionProgressProviderTest : SysuiTestCase() {
 
-    private val wrappedProgressProvider = TestUnfoldTransitionProvider()
+    private val wrappedProgressProvider = FakeUnfoldTransitionProvider()
     private val fakeHandler = FakeHandler(Looper.getMainLooper())
     private val listener = TestUnfoldProgressListener()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt
index d864d53..70ec050 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt
@@ -20,7 +20,7 @@
 import android.view.Surface
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.unfold.TestUnfoldTransitionProvider
+import com.android.systemui.unfold.FakeUnfoldTransitionProvider
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
 import com.android.systemui.unfold.updates.RotationChangeProvider
 import com.android.systemui.unfold.updates.RotationChangeProvider.RotationListener
@@ -43,14 +43,14 @@
 
     @Mock lateinit var rotationChangeProvider: RotationChangeProvider
 
-    private val sourceProvider = TestUnfoldTransitionProvider()
+    private val sourceProvider = FakeUnfoldTransitionProvider()
 
     @Mock lateinit var transitionListener: TransitionProgressListener
 
     @Captor private lateinit var rotationListenerCaptor: ArgumentCaptor<RotationListener>
 
     lateinit var progressProvider: NaturalRotationUnfoldProgressProvider
-    private lateinit var testableLooper : TestableLooper
+    private lateinit var testableLooper: TestableLooper
 
     @Before
     fun setUp() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt
index 2f29b3b..451bd24 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt
@@ -22,7 +22,7 @@
 import android.testing.TestableLooper
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.unfold.TestUnfoldTransitionProvider
+import com.android.systemui.unfold.FakeUnfoldTransitionProvider
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
 import com.android.systemui.util.mockito.any
 import org.junit.Before
@@ -42,7 +42,7 @@
 
     @Mock lateinit var sinkProvider: TransitionProgressListener
 
-    private val sourceProvider = TestUnfoldTransitionProvider()
+    private val sourceProvider = FakeUnfoldTransitionProvider()
 
     private lateinit var contentResolver: ContentResolver
     private lateinit var progressProvider: ScaleAwareTransitionProgressProvider
@@ -132,6 +132,6 @@
             durationScale.toString()
         )
 
-        animatorDurationScaleListenerCaptor.value.dispatchChange(/* selfChange= */false)
+        animatorDurationScaleListenerCaptor.value.dispatchChange(/* selfChange= */ false)
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProviderTest.kt
index 95c934e..4486402 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProviderTest.kt
@@ -23,7 +23,7 @@
 import android.testing.TestableLooper.RunWithLooper
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.unfold.TestUnfoldTransitionProvider
+import com.android.systemui.unfold.FakeUnfoldTransitionProvider
 import com.android.systemui.unfold.progress.TestUnfoldProgressListener
 import com.google.common.truth.Truth.assertThat
 import kotlin.time.Duration.Companion.seconds
@@ -43,7 +43,7 @@
 @RunWithLooper
 class ScopedUnfoldTransitionProgressProviderTest : SysuiTestCase() {
 
-    private val rootProvider = TestUnfoldTransitionProvider()
+    private val rootProvider = FakeUnfoldTransitionProvider()
     private val listener = TestUnfoldProgressListener()
     private val testScope = TestScope(UnconfinedTestDispatcher())
     private val bgThread =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt
index f484ea0..cd4d7b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt
@@ -19,7 +19,7 @@
 import android.testing.TestableLooper
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.unfold.TestUnfoldTransitionProvider
+import com.android.systemui.unfold.FakeUnfoldTransitionProvider
 import com.android.systemui.unfold.progress.TestUnfoldProgressListener
 import com.google.common.util.concurrent.MoreExecutors
 import org.junit.Before
@@ -32,7 +32,7 @@
 class UnfoldOnlyProgressProviderTest : SysuiTestCase() {
 
     private val listener = TestUnfoldProgressListener()
-    private val sourceProvider = TestUnfoldTransitionProvider()
+    private val sourceProvider = FakeUnfoldTransitionProvider()
 
     private val foldProvider = TestFoldProvider()
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt
index b4f1218..bdd4afa 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt
@@ -18,7 +18,7 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
-import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.statusbar.phone.statusBarKeyguardViewManager
@@ -27,6 +27,6 @@
 val Kosmos.alternateBouncerViewModel by Fixture {
     AlternateBouncerViewModel(
         statusBarKeyguardViewManager = statusBarKeyguardViewManager,
-        animationFlow = keyguardTransitionAnimationFlow,
+        keyguardTransitionInteractor = keyguardTransitionInteractor,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelKosmos.kt
index da2170c..2f3d3c3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelKosmos.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.media.controls.ui.viewmodel
 
 import android.content.applicationContext
+import com.android.systemui.concurrency.fakeExecutor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.media.controls.domain.pipeline.interactor.mediaControlInteractor
@@ -27,6 +28,7 @@
         MediaControlViewModel(
             applicationContext = applicationContext,
             backgroundDispatcher = testDispatcher,
+            backgroundExecutor = fakeExecutor,
             interactor = mediaControlInteractor,
             logger = mediaUiEventLogger,
         )
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
index 45b28b1..cbba80b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
@@ -46,6 +46,7 @@
 import com.android.systemui.shade.domain.interactor.shadeInteractor
 import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationStackAppearanceInteractor
 import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
+import com.android.systemui.unfold.domain.interactor.unfoldTransitionInteractor
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 @OptIn(ExperimentalCoroutinesApi::class)
@@ -81,5 +82,6 @@
         primaryBouncerToLockscreenTransitionViewModel =
             primaryBouncerToLockscreenTransitionViewModel,
         aodBurnInViewModel = aodBurnInViewModel,
+        unfoldTransitionInteractor = unfoldTransitionInteractor,
     )
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/TestUnfoldTransitionProvider.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/unfold/FakeUnfoldTransitionProvider.kt
similarity index 93%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/TestUnfoldTransitionProvider.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/unfold/FakeUnfoldTransitionProvider.kt
index 56c6245..94f0c44 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/TestUnfoldTransitionProvider.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/unfold/FakeUnfoldTransitionProvider.kt
@@ -2,7 +2,7 @@
 
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
 
-class TestUnfoldTransitionProvider : UnfoldTransitionProgressProvider, TransitionProgressListener {
+class FakeUnfoldTransitionProvider : UnfoldTransitionProgressProvider, TransitionProgressListener {
 
     private val listeners = mutableListOf<TransitionProgressListener>()
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/unfold/UnfoldTransitionProgressProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/unfold/UnfoldTransitionProgressProviderKosmos.kt
index 7c54a57..a0f5b58 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/unfold/UnfoldTransitionProgressProviderKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/unfold/UnfoldTransitionProgressProviderKosmos.kt
@@ -18,6 +18,8 @@
 
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.util.mockito.mock
 
-var Kosmos.unfoldTransitionProgressProvider by Fixture { mock<UnfoldTransitionProgressProvider>() }
+val Kosmos.fakeUnfoldTransitionProgressProvider by Fixture { FakeUnfoldTransitionProvider() }
+
+val Kosmos.unfoldTransitionProgressProvider by
+    Fixture<UnfoldTransitionProgressProvider> { fakeUnfoldTransitionProgressProvider }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractorKosmos.kt
index d03616a..6faa3b4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractorKosmos.kt
@@ -16,10 +16,14 @@
 
 package com.android.systemui.unfold.domain.interactor
 
+import com.android.systemui.common.ui.domain.interactor.configurationInteractor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.unfold.data.repository.unfoldTransitionRepository
 
 val Kosmos.unfoldTransitionInteractor by Fixture {
-    UnfoldTransitionInteractorImpl(repository = unfoldTransitionRepository)
+    UnfoldTransitionInteractor(
+        repository = unfoldTransitionRepository,
+        configurationInteractor = configurationInteractor,
+    )
 }
diff --git a/packages/SystemUI/utils/Android.bp b/packages/SystemUI/utils/Android.bp
new file mode 100644
index 0000000..1ef3816
--- /dev/null
+++ b/packages/SystemUI/utils/Android.bp
@@ -0,0 +1,32 @@
+//
+// 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 {
+    default_team: "trendy_team_system_ui_please_use_a_more_specific_subteam_if_possible_",
+    default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
+}
+
+java_library {
+    name: "SystemUI-shared-utils",
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
+    static_libs: [
+        "kotlin-stdlib",
+        "kotlinx_coroutines",
+    ],
+}
diff --git a/packages/SystemUI/utils/src/com/android/systemui/utils/coroutines/flow/FlowConflated.kt b/packages/SystemUI/utils/src/com/android/systemui/utils/coroutines/flow/FlowConflated.kt
new file mode 100644
index 0000000..ed97c60
--- /dev/null
+++ b/packages/SystemUI/utils/src/com/android/systemui/utils/coroutines/flow/FlowConflated.kt
@@ -0,0 +1,149 @@
+/*
+ * 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:OptIn(ExperimentalTypeInference::class)
+
+package com.android.systemui.utils.coroutines.flow
+
+import kotlin.experimental.ExperimentalTypeInference
+import kotlinx.coroutines.channels.ProducerScope
+import kotlinx.coroutines.channels.SendChannel
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.buffer
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.channelFlow
+import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.produceIn
+import kotlinx.coroutines.suspendCancellableCoroutine
+
+/**
+ * Creates an instance of a _cold_ [Flow] with elements that are sent to a [SendChannel] provided to
+ * the builder's [block] of code via [ProducerScope]. It allows elements to be produced by code that
+ * is running in a different context or concurrently.
+ *
+ * The resulting flow is _cold_, which means that [block] is called every time a terminal operator
+ * is applied to the resulting flow.
+ *
+ * This builder ensures thread-safety and context preservation, thus the provided [ProducerScope]
+ * can be used from any context, e.g. from a callback-based API. The resulting flow completes as
+ * soon as the code in the [block] completes. [awaitClose] should be used to keep the flow running,
+ * otherwise the channel will be closed immediately when block completes. [awaitClose] argument is
+ * called either when a flow consumer cancels the flow collection or when a callback-based API
+ * invokes [SendChannel.close] manually and is typically used to cleanup the resources after the
+ * completion, e.g. unregister a callback. Using [awaitClose] is mandatory in order to prevent
+ * memory leaks when the flow collection is cancelled, otherwise the callback may keep running even
+ * when the flow collector is already completed. To avoid such leaks, this method throws
+ * [IllegalStateException] if block returns, but the channel is not closed yet.
+ *
+ * A [conflated][conflate] channel is used. Use the [buffer] operator on the resulting flow to
+ * specify a user-defined value and to control what happens when data is produced faster than
+ * consumed, i.e. to control the back-pressure behavior.
+ *
+ * Adjacent applications of [callbackFlow], [flowOn], [buffer], and [produceIn] are always fused so
+ * that only one properly configured channel is used for execution.
+ *
+ * Example of usage that converts a multi-shot callback API to a flow. For single-shot callbacks use
+ * [suspendCancellableCoroutine].
+ *
+ * ```
+ * fun flowFrom(api: CallbackBasedApi): Flow<T> = callbackFlow {
+ *     val callback = object : Callback { // Implementation of some callback interface
+ *         override fun onNextValue(value: T) {
+ *             // To avoid blocking you can configure channel capacity using
+ *             // either buffer(Channel.CONFLATED) or buffer(Channel.UNLIMITED) to avoid overfill
+ *             trySendBlocking(value)
+ *                 .onFailure { throwable ->
+ *                     // Downstream has been cancelled or failed, can log here
+ *                 }
+ *         }
+ *         override fun onApiError(cause: Throwable) {
+ *             cancel(CancellationException("API Error", cause))
+ *         }
+ *         override fun onCompleted() = channel.close()
+ *     }
+ *     api.register(callback)
+ *     /*
+ *      * Suspends until either 'onCompleted'/'onApiError' from the callback is invoked
+ *      * or flow collector is cancelled (e.g. by 'take(1)' or because a collector's coroutine was cancelled).
+ *      * In both cases, callback will be properly unregistered.
+ *      */
+ *     awaitClose { api.unregister(callback) }
+ * }
+ * ```
+ * > The callback `register`/`unregister` methods provided by an external API must be thread-safe,
+ * > because `awaitClose` block can be called at any time due to asynchronous nature of
+ * > cancellation, even concurrently with the call of the callback.
+ *
+ * This builder is to be preferred over [callbackFlow], due to the latter's default configuration of
+ * using an internal buffer, negatively impacting system health.
+ *
+ * @see callbackFlow
+ */
+fun <T> conflatedCallbackFlow(
+    @BuilderInference block: suspend ProducerScope<T>.() -> Unit,
+): Flow<T> = callbackFlow(block).conflate()
+
+/**
+ * Creates an instance of a _cold_ [Flow] with elements that are sent to a [SendChannel] provided to
+ * the builder's [block] of code via [ProducerScope]. It allows elements to be produced by code that
+ * is running in a different context or concurrently. The resulting flow is _cold_, which means that
+ * [block] is called every time a terminal operator is applied to the resulting flow.
+ *
+ * This builder ensures thread-safety and context preservation, thus the provided [ProducerScope]
+ * can be used concurrently from different contexts. The resulting flow completes as soon as the
+ * code in the [block] and all its children completes. Use [awaitClose] as the last statement to
+ * keep it running. A more detailed example is provided in the documentation of [callbackFlow].
+ *
+ * A [conflated][conflate] channel is used. Use the [buffer] operator on the resulting flow to
+ * specify a user-defined value and to control what happens when data is produced faster than
+ * consumed, i.e. to control the back-pressure behavior.
+ *
+ * Adjacent applications of [channelFlow], [flowOn], [buffer], and [produceIn] are always fused so
+ * that only one properly configured channel is used for execution.
+ *
+ * Examples of usage:
+ * ```
+ * fun <T> Flow<T>.merge(other: Flow<T>): Flow<T> = channelFlow {
+ *     // collect from one coroutine and send it
+ *     launch {
+ *         collect { send(it) }
+ *     }
+ *     // collect and send from this coroutine, too, concurrently
+ *     other.collect { send(it) }
+ * }
+ *
+ * fun <T> contextualFlow(): Flow<T> = channelFlow {
+ *     // send from one coroutine
+ *     launch(Dispatchers.IO) {
+ *         send(computeIoValue())
+ *     }
+ *     // send from another coroutine, concurrently
+ *     launch(Dispatchers.Default) {
+ *         send(computeCpuValue())
+ *     }
+ * }
+ * ```
+ *
+ * This builder is to be preferred over [channelFlow], due to the latter's default configuration of
+ * using an internal buffer, negatively impacting system health.
+ *
+ * @see channelFlow
+ */
+fun <T> conflatedChannelFlow(
+    @BuilderInference block: suspend ProducerScope<T>.() -> Unit,
+): Flow<T> = channelFlow(block).conflate()
diff --git a/packages/SystemUI/utils/src/com/android/systemui/utils/coroutines/flow/LatestConflated.kt b/packages/SystemUI/utils/src/com/android/systemui/utils/coroutines/flow/LatestConflated.kt
new file mode 100644
index 0000000..5f8c660
--- /dev/null
+++ b/packages/SystemUI/utils/src/com/android/systemui/utils/coroutines/flow/LatestConflated.kt
@@ -0,0 +1,120 @@
+/*
+ * 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:OptIn(ExperimentalCoroutinesApi::class, ExperimentalTypeInference::class)
+
+package com.android.systemui.utils.coroutines.flow
+
+import kotlin.experimental.ExperimentalTypeInference
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.FlowCollector
+import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.mapLatest
+import kotlinx.coroutines.flow.transformLatest
+
+/**
+ * Returns a flow that emits elements from the original flow transformed by [transform] function.
+ * When the original flow emits a new value, computation of the [transform] block for previous value
+ * is cancelled.
+ *
+ * For example, the following flow:
+ * ```
+ * flow {
+ *     emit("a")
+ *     delay(100)
+ *     emit("b")
+ * }.mapLatest { value ->
+ *     println("Started computing $value")
+ *     delay(200)
+ *     "Computed $value"
+ * }
+ * ```
+ *
+ * will print "Started computing a" and "Started computing b", but the resulting flow will contain
+ * only "Computed b" value.
+ *
+ * This operator is [conflated][conflate] by default, and as such should be preferred over usage of
+ * [mapLatest], due to the latter's default configuration of using an internal buffer, negatively
+ * impacting system health.
+ *
+ * @see mapLatest
+ */
+fun <T, R> Flow<T>.mapLatestConflated(@BuilderInference transform: suspend (T) -> R): Flow<R> =
+    mapLatest(transform).conflate()
+
+/**
+ * Returns a flow that switches to a new flow produced by [transform] function every time the
+ * original flow emits a value. When the original flow emits a new value, the previous flow produced
+ * by `transform` block is cancelled.
+ *
+ * For example, the following flow:
+ * ```
+ * flow {
+ *     emit("a")
+ *     delay(100)
+ *     emit("b")
+ * }.flatMapLatest { value ->
+ *     flow {
+ *         emit(value)
+ *         delay(200)
+ *         emit(value + "_last")
+ *     }
+ * }
+ * ```
+ *
+ * produces `a b b_last`
+ *
+ * This operator is [conflated][conflate] by default, and as such should be preferred over usage of
+ * [flatMapLatest], due to the latter's default configuration of using an internal buffer,
+ * negatively impacting system health.
+ *
+ * @see flatMapLatest
+ */
+fun <T, R> Flow<T>.flatMapLatestConflated(
+    @BuilderInference transform: suspend (T) -> Flow<R>,
+): Flow<R> = flatMapLatest(transform).conflate()
+
+/**
+ * Returns a flow that produces element by [transform] function every time the original flow emits a
+ * value. When the original flow emits a new value, the previous `transform` block is cancelled,
+ * thus the name `transformLatest`.
+ *
+ * For example, the following flow:
+ * ```
+ * flow {
+ *     emit("a")
+ *     delay(100)
+ *     emit("b")
+ * }.transformLatest { value ->
+ *     emit(value)
+ *     delay(200)
+ *     emit(value + "_last")
+ * }
+ * ```
+ *
+ * produces `a b b_last`.
+ *
+ * This operator is [conflated][conflate] by default, and as such should be preferred over usage of
+ * [transformLatest], due to the latter's default configuration of using an internal buffer,
+ * negatively impacting system health.
+ *
+ * @see transformLatest
+ */
+fun <T, R> Flow<T>.transformLatestConflated(
+    @BuilderInference transform: suspend FlowCollector<R>.(T) -> Unit,
+): Flow<R> = transformLatest(transform).conflate()
diff --git a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
index 23e269a..cbbce1a 100644
--- a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
+++ b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
@@ -19,6 +19,7 @@
 import static android.app.WallpaperManager.FLAG_LOCK;
 import static android.app.WallpaperManager.FLAG_SYSTEM;
 import static android.app.WallpaperManager.ORIENTATION_UNKNOWN;
+import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
 
 import static com.android.wallpaperbackup.WallpaperEventLogger.ERROR_INELIGIBLE;
 import static com.android.wallpaperbackup.WallpaperEventLogger.ERROR_NO_METADATA;
@@ -39,6 +40,7 @@
 import android.content.SharedPreferences;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
+import android.graphics.BitmapFactory;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
@@ -109,22 +111,16 @@
     static final String LOCK_WALLPAPER_STAGE = "wallpaper-lock-stage";
     @VisibleForTesting
     static final String WALLPAPER_INFO_STAGE = "wallpaper-info-stage";
-
     @VisibleForTesting
     static final String WALLPAPER_BACKUP_DEVICE_INFO_STAGE = "wallpaper-backup-device-info-stage";
-
     static final String EMPTY_SENTINEL = "empty";
     static final String QUOTA_SENTINEL = "quota";
-
     // Shared preferences constants.
     static final String PREFS_NAME = "wbprefs.xml";
     static final String SYSTEM_GENERATION = "system_gen";
     static final String LOCK_GENERATION = "lock_gen";
 
-    /**
-     * An approximate area threshold to compare device dimension similarity
-     */
-    static final int AREA_THRESHOLD = 50; // TODO (b/327637867): determine appropriate threshold
+    static final float DEFAULT_ACCEPTABLE_PARALLAX = 0.2f;
 
     // If this file exists, it means we exceeded our quota last time
     private File mQuotaFile;
@@ -336,7 +332,6 @@
         mEventLogger.onSystemImageWallpaperBackupFailed(error);
     }
 
-
     private void backupLockWallpaperFileIfItExists(SharedPreferences sharedPrefs,
             boolean lockChanged, int lockGeneration, FullBackupDataOutput data) throws IOException {
         final File lockImageStage = new File(getFilesDir(), LOCK_WALLPAPER_STAGE);
@@ -409,6 +404,16 @@
         }
     }
 
+    private static String readText(TypedXmlPullParser parser)
+            throws IOException, XmlPullParserException {
+        String result = "";
+        if (parser.next() == XmlPullParser.TEXT) {
+            result = parser.getText();
+            parser.nextTag();
+        }
+        return result;
+    }
+
     @VisibleForTesting
     // fullBackupFile is final, so we intercept backups here in tests.
     protected void backupFile(File file, FullBackupDataOutput data) {
@@ -438,18 +443,10 @@
         boolean lockImageStageExists = lockImageStage.exists();
 
         try {
-            // Parse the device dimensions of the source device and compare with target to
-            // to identify whether we need to skip the remainder of the restore process
+            // Parse the device dimensions of the source device
             Pair<Point, Point> sourceDeviceDimensions = parseDeviceDimensions(
                     deviceDimensionsStage);
 
-            Point targetDeviceDimensions = getScreenDimensions();
-            if (sourceDeviceDimensions != null && targetDeviceDimensions != null
-                    && isSourceDeviceSignificantlySmallerThanTarget(sourceDeviceDimensions.first,
-                    targetDeviceDimensions)) {
-                Slog.d(TAG, "The source device is significantly smaller than target");
-            }
-
             // First parse the live component name so that we know for logging if we care about
             // logging errors with the image restore.
             ComponentName wpService = parseWallpaperComponent(infoStage, "wp");
@@ -466,9 +463,10 @@
             // to back up the original image on the source device, or there was no user-supplied
             // wallpaper image present.
             if (lockImageStageExists) {
-                restoreFromStage(lockImageStage, infoStage, "kwp", FLAG_LOCK);
+                restoreFromStage(lockImageStage, infoStage, "kwp", FLAG_LOCK,
+                        sourceDeviceDimensions);
             }
-            restoreFromStage(imageStage, infoStage, "wp", sysWhich);
+            restoreFromStage(imageStage, infoStage, "wp", sysWhich, sourceDeviceDimensions);
 
             // And reset to the wallpaper service we should be using
             if (mLockHasLiveComponent) {
@@ -543,16 +541,6 @@
         }
     }
 
-    private static String readText(TypedXmlPullParser parser)
-            throws IOException, XmlPullParserException {
-        String result = "";
-        if (parser.next() == XmlPullParser.TEXT) {
-            result = parser.getText();
-            parser.nextTag();
-        }
-        return result;
-    }
-
     @VisibleForTesting
     void updateWallpaperComponent(ComponentName wpService, int which)
             throws IOException {
@@ -578,10 +566,13 @@
         }
     }
 
-    private void restoreFromStage(File stage, File info, String hintTag, int which)
+    private void restoreFromStage(File stage, File info, String hintTag, int which,
+            Pair<Point, Point> sourceDeviceDimensions)
             throws IOException {
         if (stage.exists()) {
             if (multiCrop()) {
+                // TODO(b/332937943): implement offset adjustment by manually adjusting crop to
+                //  adhere to device aspect ratio
                 SparseArray<Rect> cropHints = parseCropHints(info, hintTag);
                 if (cropHints != null) {
                     Slog.i(TAG, "Got restored wallpaper; applying which=" + which
@@ -601,7 +592,6 @@
                 }
                 return;
             }
-
             // Parse the restored info file to find the crop hint.  Note that this currently
             // relies on a priori knowledge of the wallpaper info file schema.
             Rect cropHint = parseCropHint(info, hintTag);
@@ -609,8 +599,33 @@
                 Slog.i(TAG, "Got restored wallpaper; applying which=" + which
                         + "; cropHint = " + cropHint);
                 try (FileInputStream in = new FileInputStream(stage)) {
-                    mWallpaperManager.setStream(in, cropHint.isEmpty() ? null : cropHint, true,
-                            which);
+
+                    if (sourceDeviceDimensions != null && sourceDeviceDimensions.first != null) {
+                        BitmapFactory.Options options = new BitmapFactory.Options();
+                        options.inJustDecodeBounds = true;
+                        ParcelFileDescriptor pdf = ParcelFileDescriptor.open(stage, MODE_READ_ONLY);
+                        BitmapFactory.decodeFileDescriptor(pdf.getFileDescriptor(),
+                                null, options);
+                        Point bitmapSize = new Point(options.outWidth, options.outHeight);
+                        Point sourceDeviceSize = new Point(sourceDeviceDimensions.first.x,
+                                sourceDeviceDimensions.first.y);
+                        Point targetDeviceDimensions = getScreenDimensions();
+
+                        // TODO: for now we handle only the case where the target device has smaller
+                        // aspect ratio than the source device i.e. the target device is more narrow
+                        // than the source device
+                        if (isTargetMoreNarrowThanSource(targetDeviceDimensions,
+                                sourceDeviceSize)) {
+                            Rect adjustedCrop = findNewCropfromOldCrop(cropHint,
+                                    sourceDeviceDimensions.first, true, targetDeviceDimensions,
+                                    bitmapSize, true);
+
+                            cropHint.set(adjustedCrop);
+                        }
+                    }
+
+                    mWallpaperManager.setStream(in, cropHint.isEmpty() ? null : cropHint,
+                            true, which);
 
                     // And log the success
                     if ((which & FLAG_SYSTEM) > 0) {
@@ -629,6 +644,209 @@
         }
     }
 
+    /**
+     * This method computes the crop of the stored wallpaper to preserve its center point as the
+     * user had set it in the previous device.
+     *
+     * The algorithm involves first computing the original crop of the user (without parallax). Then
+     * manually adjusting the user's original crop to respect the current device's aspect ratio
+     * (thereby preserving the center point). Then finally, adding any leftover image real-estate
+     * (i.e. space left over on the horizontal axis) to add parallax effect. Parallax is only added
+     * if was present in the old device's settings.
+     *
+     */
+    private Rect findNewCropfromOldCrop(Rect oldCrop, Point oldDisplaySize, boolean oldRtl,
+            Point newDisplaySize, Point bitmapSize, boolean newRtl) {
+        Rect cropWithoutParallax = withoutParallax(oldCrop, oldDisplaySize, oldRtl, bitmapSize);
+        oldCrop = oldCrop.isEmpty() ? new Rect(0, 0, bitmapSize.x, bitmapSize.y) : oldCrop;
+        float oldParallaxAmount = ((float) oldCrop.width() / cropWithoutParallax.width()) - 1;
+
+        Rect newCropWithSameCenterWithoutParallax = sameCenter(newDisplaySize, bitmapSize,
+                cropWithoutParallax);
+
+        Rect newCrop = newCropWithSameCenterWithoutParallax;
+
+        // calculate the amount of left-over space there is in the image after adjusting the crop
+        // from the above operation i.e. in a rtl configuration, this is the remaining space in the
+        // image after subtracting the new crop's right edge coordinate from the image itself, and
+        // for ltr, its just the new crop's left edge coordinate (as it's the distance from the
+        // beginning of the image)
+        int widthAvailableForParallaxOnTheNewDevice =
+                (newRtl) ? newCrop.left : bitmapSize.x - newCrop.right;
+
+        // calculate relatively how much this available space is as a fraction of the total cropped
+        // image
+        float availableParallaxAmount =
+                (float) widthAvailableForParallaxOnTheNewDevice / newCrop.width();
+
+        float minAcceptableParallax = Math.min(DEFAULT_ACCEPTABLE_PARALLAX, oldParallaxAmount);
+
+        if (DEBUG) {
+            Slog.d(TAG, "- cropWithoutParallax: " + cropWithoutParallax);
+            Slog.d(TAG, "- oldParallaxAmount: " + oldParallaxAmount);
+            Slog.d(TAG, "- newCropWithSameCenterWithoutParallax: "
+                    + newCropWithSameCenterWithoutParallax);
+            Slog.d(TAG, "- widthAvailableForParallaxOnTheNewDevice: "
+                    + widthAvailableForParallaxOnTheNewDevice);
+            Slog.d(TAG, "- availableParallaxAmount: " + availableParallaxAmount);
+            Slog.d(TAG, "- minAcceptableParallax: " + minAcceptableParallax);
+            Slog.d(TAG, "- oldCrop: " + oldCrop);
+            Slog.d(TAG, "- oldDisplaySize: " + oldDisplaySize);
+            Slog.d(TAG, "- oldRtl: " + oldRtl);
+            Slog.d(TAG, "- newDisplaySize: " + newDisplaySize);
+            Slog.d(TAG, "- bitmapSize: " + bitmapSize);
+            Slog.d(TAG, "- newRtl: " + newRtl);
+        }
+        if (availableParallaxAmount >= minAcceptableParallax) {
+            // but in any case, don't put more parallax than the amount of the old device
+            float parallaxToAdd = Math.min(availableParallaxAmount, oldParallaxAmount);
+
+            int widthToAddForParallax = (int) (newCrop.width() * parallaxToAdd);
+            if (DEBUG) {
+                Slog.d(TAG, "- parallaxToAdd: " + parallaxToAdd);
+                Slog.d(TAG, "- widthToAddForParallax: " + widthToAddForParallax);
+            }
+            if (newRtl) {
+                newCrop.left -= widthToAddForParallax;
+            } else {
+                newCrop.right += widthToAddForParallax;
+            }
+        }
+        return newCrop;
+    }
+
+    /**
+     * This method computes the original crop of the user without parallax.
+     *
+     * NOTE: When the user sets the wallpaper with a specific crop, there may additional image added
+     * to the crop to support parallax. In order to determine the user's actual crop the parallax
+     * must be removed if it exists.
+     */
+    Rect withoutParallax(Rect crop, Point displaySize, boolean rtl, Point bitmapSize) {
+        // in the case an image's crop is not set, we assume the image itself is cropped
+        if (crop.isEmpty()) {
+            crop = new Rect(0, 0, bitmapSize.x, bitmapSize.y);
+        }
+
+        if (DEBUG) {
+            Slog.w(TAG, "- crop: " + crop);
+        }
+
+        Rect adjustedCrop = new Rect(crop);
+        float suggestedDisplayRatio = (float) displaySize.x / displaySize.y;
+
+        // here we calculate the width of the wallpaper image such that it has the same aspect ratio
+        // as the given display i.e. the width of the image on a single page of the device without
+        // parallax (i.e. displaySize will correspond to the display the crop was originally set on)
+        int wallpaperWidthWithoutParallax = (int) (0.5f + (float) displaySize.x * crop.height()
+                / displaySize.y);
+        // subtracting wallpaperWidthWithoutParallax from the wallpaper crop gives the amount of
+        // parallax added
+        int widthToRemove = Math.max(0, crop.width() - wallpaperWidthWithoutParallax);
+
+        if (DEBUG) {
+            Slog.d(TAG, "- adjustedCrop: " + adjustedCrop);
+            Slog.d(TAG, "- suggestedDisplayRatio: " + suggestedDisplayRatio);
+            Slog.d(TAG, "- wallpaperWidthWithoutParallax: " + wallpaperWidthWithoutParallax);
+            Slog.d(TAG, "- widthToRemove: " + widthToRemove);
+        }
+        if (rtl) {
+            adjustedCrop.left += widthToRemove;
+        } else {
+            adjustedCrop.right -= widthToRemove;
+        }
+
+        if (DEBUG) {
+            Slog.d(TAG, "- adjustedCrop: " + crop);
+        }
+        return adjustedCrop;
+    }
+
+    /**
+     * This method computes a new crop based on the given crop in order to preserve the center point
+     * of the given crop on the provided displaySize. This is only for the case where the device
+     * displaySize has a smaller aspect ratio than the cropped image.
+     *
+     * NOTE: If the width to height ratio is less in the device display than cropped image
+     * this means the aspect ratios are off and there will be distortions in the image
+     * if the image is applied to the current display (i.e. the image will be skewed ->
+     * pixels in the image will not align correctly with the same pixels in the image that are
+     * above them)
+     */
+    Rect sameCenter(Point displaySize, Point bitmapSize, Rect crop) {
+
+        // in the case an image's crop is not set, we assume the image itself is cropped
+        if (crop.isEmpty()) {
+            crop = new Rect(0, 0, bitmapSize.x, bitmapSize.y);
+        }
+
+        float screenRatio = (float) displaySize.x / displaySize.y;
+        float cropRatio = (float) crop.width() / crop.height();
+
+        Rect adjustedCrop = new Rect(crop);
+
+        if (screenRatio < cropRatio) {
+            // the screen is more narrow than the image, and as such, the image will need to be
+            // zoomed in till it fits in the vertical axis. Due to this, we need to manually adjust
+            // the image's crop in order for it to fit into the screen without having the framework
+            // do it (since the framework left aligns the image after zooming)
+
+            // Calculate the height of the adjusted wallpaper crop so it respects the aspect ratio
+            // of the device. To calculate the height, we will use the width of the current crop.
+            // This is so we find the largest height possible which also respects the device aspect
+            // ratio.
+            int heightToAdd = (int) (0.5f + crop.width() / screenRatio - crop.height());
+
+            // Calculate how much extra image space available that can be used to adjust
+            // the crop. If this amount is less than heightToAdd, from above, then that means we
+            // can't use heightToAdd. Instead we will need to use the maximum possible height, which
+            // is the height of the original bitmap. NOTE: the bitmap height may be different than
+            // the crop.
+            // since there is no guarantee to have height available on both sides
+            // (e.g. the available height might be fully at the bottom), grab the minimum
+            int availableHeight = 2 * Math.min(crop.top, bitmapSize.y - crop.bottom);
+            int actualHeightToAdd = Math.min(heightToAdd, availableHeight);
+
+            // half of the additional height is added to the top and bottom of the crop
+            adjustedCrop.top -= actualHeightToAdd / 2 + actualHeightToAdd % 2;
+            adjustedCrop.bottom += actualHeightToAdd / 2;
+
+            // Calculate the width of the adjusted crop. Initially we used the fixed width of the
+            // crop to calculate the heightToAdd, but since this height may be invalid (based on
+            // the calculation above) we calculate the width again instead of using the fixed width,
+            // using the adjustedCrop's updated height.
+            int widthToRemove = (int) (0.5f + crop.width() - adjustedCrop.height() * screenRatio);
+
+            // half of the additional width is subtracted from the left and right side of the crop
+            int widthToRemoveLeft = widthToRemove / 2;
+            int widthToRemoveRight = widthToRemove / 2 + widthToRemove % 2;
+
+            adjustedCrop.left += widthToRemoveLeft;
+            adjustedCrop.right -= widthToRemoveRight;
+
+            if (DEBUG) {
+                Slog.d(TAG, "cropRatio: " + cropRatio);
+                Slog.d(TAG, "screenRatio: " + screenRatio);
+                Slog.d(TAG, "heightToAdd: " + heightToAdd);
+                Slog.d(TAG, "actualHeightToAdd: " + actualHeightToAdd);
+                Slog.d(TAG, "availableHeight: " + availableHeight);
+                Slog.d(TAG, "widthToRemove: " + widthToRemove);
+                Slog.d(TAG, "adjustedCrop: " + adjustedCrop);
+            }
+
+            return adjustedCrop;
+        }
+
+        return adjustedCrop;
+    }
+
+    private boolean isTargetMoreNarrowThanSource(Point targetDisplaySize, Point srcDisplaySize) {
+        float targetScreenRatio = (float) targetDisplaySize.x / targetDisplaySize.y;
+        float srcScreenRatio = (float) srcDisplaySize.x / srcDisplaySize.y;
+
+        return (targetScreenRatio < srcScreenRatio);
+    }
+
     private void logRestoreErrorIfNoLiveComponent(int which, String error) {
         if (mSystemHasLiveComponent) {
             return;
@@ -644,6 +862,7 @@
             mEventLogger.onLockImageWallpaperRestoreFailed(error);
         }
     }
+
     private Rect parseCropHint(File wallpaperInfo, String sectionTag) {
         Rect cropHint = new Rect();
         try (FileInputStream stream = new FileInputStream(wallpaperInfo)) {
@@ -681,7 +900,7 @@
                 if (type != XmlPullParser.START_TAG) continue;
                 String tag = parser.getName();
                 if (!sectionTag.equals(tag)) continue;
-                for (Pair<Integer, String> pair: List.of(
+                for (Pair<Integer, String> pair : List.of(
                         new Pair<>(WallpaperManager.PORTRAIT, "Portrait"),
                         new Pair<>(WallpaperManager.LANDSCAPE, "Landscape"),
                         new Pair<>(WallpaperManager.SQUARE_PORTRAIT, "SquarePortrait"),
@@ -907,22 +1126,6 @@
         return internalDisplays;
     }
 
-    /**
-     * This method compares the source and target dimensions, and returns true if there is a
-     * significant difference in area between them and the source dimensions are smaller than the
-     * target dimensions.
-     *
-     * @param sourceDimensions is the dimensions of the source device
-     * @param targetDimensions is the dimensions of the target device
-     */
-    @VisibleForTesting
-    boolean isSourceDeviceSignificantlySmallerThanTarget(Point sourceDimensions,
-            Point targetDimensions) {
-        int rawAreaDelta = (targetDimensions.x * targetDimensions.y)
-                - (sourceDimensions.x * sourceDimensions.y);
-        return rawAreaDelta > AREA_THRESHOLD;
-    }
-
     @VisibleForTesting
     boolean isDeviceInRestore() {
         try {
diff --git a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
index ec9223c..3ecdf3f 100644
--- a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
+++ b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
@@ -59,7 +59,6 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.FileUtils;
 import android.os.ParcelFileDescriptor;
@@ -841,26 +840,6 @@
         testParseCropHints(testMap);
     }
 
-    @Test
-    public void test_sourceDimensionsAreLargerThanTarget() {
-        // source device is larger than target, expecting to get false
-        Point sourceDimensions = new Point(2208, 1840);
-        Point targetDimensions = new Point(1080, 2092);
-        boolean isSourceSmaller = mWallpaperBackupAgent
-                .isSourceDeviceSignificantlySmallerThanTarget(sourceDimensions, targetDimensions);
-        assertThat(isSourceSmaller).isEqualTo(false);
-    }
-
-    @Test
-    public void test_sourceDimensionsMuchSmallerThanTarget() {
-        // source device is smaller than target, expecting to get true
-        Point sourceDimensions = new Point(1080, 2092);
-        Point targetDimensions = new Point(2208, 1840);
-        boolean isSourceSmaller = mWallpaperBackupAgent
-                .isSourceDeviceSignificantlySmallerThanTarget(sourceDimensions, targetDimensions);
-        assertThat(isSourceSmaller).isEqualTo(true);
-    }
-
     private void testParseCropHints(Map<Integer, Rect> testMap) throws Exception {
         assumeTrue(multiCrop());
         mockRestoredStaticWallpaperFile(testMap);
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index be2ad21..d279bd5 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -852,11 +852,9 @@
         final int pointerIdBits = (1 << pointerId);
         if (mSendHoverEnterAndMoveDelayed.isPending()) {
             // If we have not delivered the enter schedule an exit.
-            if (Flags.resetHoverEventTimerOnActionUp()) {
-                // We cancel first to reset the time window so that the user has the full amount of
-                // time to do a multi tap.
-                mSendHoverEnterAndMoveDelayed.repost();
-            }
+            // We cancel first to reset the time window so that the user has the full amount of
+            // time to do a multi tap.
+            mSendHoverEnterAndMoveDelayed.repost();
             mSendHoverExitDelayed.post(event, rawEvent, pointerIdBits, policyFlags);
         } else {
             // The user is touch exploring so we send events for end.
@@ -1601,7 +1599,7 @@
                                 + " pointers down.");
                 return;
             }
-            if (Flags.resetHoverEventTimerOnActionUp() && mEvents.size() == 0) {
+            if (mEvents.size() == 0) {
                 return;
             }
             // Send an accessibility event to announce the touch exploration start.
diff --git a/services/autofill/bugfixes.aconfig b/services/autofill/bugfixes.aconfig
index ced10fb..70ecc055 100644
--- a/services/autofill/bugfixes.aconfig
+++ b/services/autofill/bugfixes.aconfig
@@ -41,3 +41,10 @@
   description: "Use weak reference to address binder leak problem"
   bug: "307972253"
 }
+
+flag {
+  name: "include_last_focused_id_and_session_id_in_client_state"
+  namespace: "autofill"
+  description: "Include the current view id and session id into the FillEventHistory as part of ClientState"
+  bug: "334141398"
+}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 3f3ff4a..3a38406 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -5188,11 +5188,13 @@
                     String[] exception =  resultData.getStringArray(
                             CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION);
                     if (exception != null && exception.length >= 2) {
+                        String errType = exception[0];
+                        String errMsg = exception[1];
                         Slog.w(TAG, "Credman bottom sheet from pinned "
-                                + "entry failed with: + " + exception[0] + " , "
-                                + exception[1]);
+                                + "entry failed with: + " + errType + " , "
+                                + errMsg);
                         sendCredentialManagerResponseToApp(/*response=*/ null,
-                                new GetCredentialException(exception[0], exception[1]),
+                                new GetCredentialException(errType, errMsg),
                                 mAutofillId);
                     }
                 } else {
diff --git a/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java b/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java
index cfb7f337..af49df6 100644
--- a/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java
+++ b/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java
@@ -56,6 +56,7 @@
 import android.util.SparseBooleanArray;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.CollectionUtils;
 import com.android.server.companion.association.AssociationStore;
 
 import java.io.PrintWriter;
@@ -1031,6 +1032,9 @@
     public void sendDevicePresenceEventOnUnlocked(int userId) {
         final List<DevicePresenceEvent> deviceEvents = getPendingDevicePresenceEventsByUserId(
                 userId);
+        if (CollectionUtils.isEmpty(deviceEvents)) {
+            return;
+        }
         final List<ObservableUuid> observableUuids =
                 mObservableUuidStore.getObservableUuidsForUser(userId);
         // Notify and bind the app after the phone is unlocked.
@@ -1068,7 +1072,7 @@
             }
         }
 
-        clearPendingDevicePresenceEventsByUserId(userId);
+        removePendingDevicePresenceEventsByUserId(userId);
     }
 
     private List<DevicePresenceEvent> getPendingDevicePresenceEventsByUserId(int userId) {
@@ -1077,9 +1081,11 @@
         }
     }
 
-    private void clearPendingDevicePresenceEventsByUserId(int userId) {
+    private void removePendingDevicePresenceEventsByUserId(int userId) {
         synchronized (mPendingDevicePresenceEvents) {
-            mPendingDevicePresenceEvents.get(userId).clear();
+            if (mPendingDevicePresenceEvents.contains(userId)) {
+                mPendingDevicePresenceEvents.remove(userId);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java b/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java
index 4694e9f..2067ad4 100644
--- a/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java
+++ b/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java
@@ -306,6 +306,7 @@
         }
     }
 
+    @GuardedBy("mSensitiveContentProtectionLock")
     private void updateAppsThatShouldBlockScreenCapture() {
         RankingMap rankingMap;
         try {
@@ -318,6 +319,7 @@
         updateAppsThatShouldBlockScreenCapture(rankingMap);
     }
 
+    @GuardedBy("mSensitiveContentProtectionLock")
     private void updateAppsThatShouldBlockScreenCapture(RankingMap rankingMap) {
         StatusBarNotification[] notifications;
         try {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5ec6b72..ee5d49b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -9189,6 +9189,11 @@
     private class MyBinderProxyCountEventListener implements BinderProxyCountEventListener {
         @Override
         public void onLimitReached(int uid) {
+            // Spawn a new thread for the dump as it'll take long time.
+            new Thread(() -> handleLimitReached(uid), "BinderProxy Dump: " + uid).start();
+        }
+
+        private void handleLimitReached(int uid) {
             Slog.wtf(TAG, "Uid " + uid + " sent too many Binders to uid "
                     + Process.myUid());
             BinderProxy.dumpProxyDebugInfo();
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 0e22ef1..c11fbe1 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -7910,6 +7910,7 @@
         DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.add(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
         DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.add(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
         DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.add(AudioSystem.DEVICE_OUT_LINE);
+        DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.add(AudioSystem.DEVICE_OUT_HEARING_AID);
         DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.addAll(AudioSystem.DEVICE_OUT_ALL_A2DP_SET);
         DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.addAll(AudioSystem.DEVICE_OUT_ALL_BLE_SET);
         DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.addAll(AudioSystem.DEVICE_OUT_ALL_USB_SET);
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 1c169a0..5c93181 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -1766,7 +1766,8 @@
                 loadDensityMapping(config);
                 loadBrightnessDefaultFromDdcXml(config);
                 loadBrightnessConstraintsFromConfigXml();
-                if (mFlags.isEvenDimmerEnabled()) {
+                if (mFlags.isEvenDimmerEnabled() && mContext.getResources().getBoolean(
+                        com.android.internal.R.bool.config_evenDimmerEnabled)) {
                     mEvenDimmerBrightnessData = EvenDimmerBrightnessData.loadConfig(config);
                 }
                 loadBrightnessMap(config);
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 8f1277b..4e709a7 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -5271,5 +5271,13 @@
         public ExternalDisplayStatsService getExternalDisplayStatsService() {
             return mExternalDisplayStatsService;
         }
+
+        /**
+         * Called on external display is ready to be enabled.
+         */
+        @Override
+        public void onExternalDisplayReadyToBeEnabled(int displayId) {
+            mDisplayModeDirector.onExternalDisplayReadyToBeEnabled(displayId);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/display/ExternalDisplayPolicy.java b/services/core/java/com/android/server/display/ExternalDisplayPolicy.java
index b24caf4..3c2918f 100644
--- a/services/core/java/com/android/server/display/ExternalDisplayPolicy.java
+++ b/services/core/java/com/android/server/display/ExternalDisplayPolicy.java
@@ -91,6 +91,8 @@
 
         @NonNull
         ExternalDisplayStatsService getExternalDisplayStatsService();
+
+        void onExternalDisplayReadyToBeEnabled(int displayId);
     }
 
     @NonNull
@@ -185,6 +187,10 @@
             return;
         }
 
+        if (enabled) {
+            mInjector.onExternalDisplayReadyToBeEnabled(logicalDisplay.getDisplayIdLocked());
+        }
+
         mLogicalDisplayMapper.setDisplayEnabledLocked(logicalDisplay, enabled);
     }
 
@@ -217,6 +223,7 @@
         if ((Build.IS_ENG || Build.IS_USERDEBUG)
                 && SystemProperties.getBoolean(ENABLE_ON_CONNECT, false)) {
             Slog.w(TAG, "External display is enabled by default, bypassing user consent.");
+            mInjector.onExternalDisplayReadyToBeEnabled(logicalDisplay.getDisplayIdLocked());
             mInjector.sendExternalDisplayEventLocked(logicalDisplay, EVENT_DISPLAY_CONNECTED);
             return;
         } else {
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index a862b6e..9064763 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -694,6 +694,13 @@
     }
 
     /**
+     * Called when external display is ready to be enabled.
+     */
+    public void onExternalDisplayReadyToBeEnabled(int displayId) {
+        mDisplayObserver.onExternalDisplayReadyToBeEnabled(displayId);
+    }
+
+    /**
      * Listens for changes refresh rate coordination.
      */
     public interface DesiredDisplayModeSpecsListener {
@@ -1379,6 +1386,13 @@
             }
         }
 
+
+        void onExternalDisplayReadyToBeEnabled(int displayId) {
+            DisplayInfo displayInfo = getDisplayInfo(displayId);
+            updateDisplaysPeakRefreshRateAndResolution(displayInfo);
+            addDisplaysSynchronizedPeakRefreshRate(displayInfo);
+        }
+
         @Override
         public void onDisplayAdded(int displayId) {
             updateVrrStatus(displayId);
@@ -1386,8 +1400,6 @@
             updateDisplayModes(displayId, displayInfo);
             updateLayoutLimitedFrameRate(displayId, displayInfo);
             updateUserSettingDisplayPreferredSize(displayInfo);
-            updateDisplaysPeakRefreshRateAndResolution(displayInfo);
-            addDisplaysSynchronizedPeakRefreshRate(displayInfo);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 6b33199..2583d73 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -284,12 +284,11 @@
     final Resources mRes;
     private final Handler mHandler;
 
-    /**
-     * TODO(b/329163064): Remove this field.
-     */
-    @NonNull
     @MultiUserUnawareField
-    private InputMethodSettings mSettings;
+    @UserIdInt
+    @GuardedBy("ImfLock.class")
+    private int mCurrentUserId;
+
     @MultiUserUnawareField
     final SettingsObserver mSettingsObserver;
     final WindowManagerInternal mWindowManagerInternal;
@@ -494,7 +493,7 @@
     @GuardedBy("ImfLock.class")
     @Nullable
     InputMethodInfo queryInputMethodForCurrentUserLocked(@NonNull String imeId) {
-        return mSettings.getMethodMap().get(imeId);
+        return InputMethodSettingsRepository.get(mCurrentUserId).getMethodMap().get(imeId);
     }
 
     /**
@@ -815,7 +814,8 @@
                     InputMethodManager.invalidateLocalStylusHandwritingAvailabilityCaches();
                 } else {
                     boolean enabledChanged = false;
-                    String newEnabled = mSettings.getEnabledInputMethodsStr();
+                    String newEnabled = InputMethodSettingsRepository.get(mCurrentUserId)
+                            .getEnabledInputMethodsStr();
                     if (!mLastEnabled.equals(newEnabled)) {
                         mLastEnabled = newEnabled;
                         enabledChanged = true;
@@ -847,9 +847,11 @@
                 // sender userId can be a real user ID or USER_ALL.
                 final int senderUserId = pendingResult.getSendingUserId();
                 if (senderUserId != UserHandle.USER_ALL) {
-                    if (senderUserId != mSettings.getUserId()) {
-                        // A background user is trying to hide the dialog. Ignore.
-                        return;
+                    synchronized (ImfLock.class) {
+                        if (senderUserId != mCurrentUserId) {
+                            // A background user is trying to hide the dialog. Ignore.
+                            return;
+                        }
                     }
                 }
                 mMenuController.hideInputMethodMenu();
@@ -880,9 +882,6 @@
                                 AdditionalSubtypeMapRepository.get(userId),
                                 DirectBootAwareness.AUTO);
                 InputMethodSettingsRepository.put(userId, settings);
-                if (userId == mSettings.getUserId()) {
-                    mSettings = settings;
-                }
             }
             postInputMethodSettingUpdatedLocked(true /* resetDefaultEnabledIme */);
             // If the locale is changed, needs to reset the default ime
@@ -944,7 +943,7 @@
         @GuardedBy("ImfLock.class")
         private boolean isChangingPackagesOfCurrentUserLocked() {
             final int userId = getChangingUserId();
-            final boolean retval = userId == mSettings.getUserId();
+            final boolean retval = userId == mCurrentUserId;
             if (DEBUG) {
                 if (!retval) {
                     Slog.d(TAG, "--- ignore this call back from a background user: " + userId);
@@ -959,8 +958,10 @@
                 if (!isChangingPackagesOfCurrentUserLocked()) {
                     return false;
                 }
-                String curInputMethodId = mSettings.getSelectedInputMethod();
-                final List<InputMethodInfo> methodList = mSettings.getMethodList();
+                final InputMethodSettings settings =
+                        InputMethodSettingsRepository.get(mCurrentUserId);
+                String curInputMethodId = settings.getSelectedInputMethod();
+                final List<InputMethodInfo> methodList = settings.getMethodList();
                 final int numImes = methodList.size();
                 if (curInputMethodId != null) {
                     for (int i = 0; i < numImes; i++) {
@@ -1077,7 +1078,7 @@
         private void onFinishPackageChangesInternal() {
             synchronized (ImfLock.class) {
                 final int userId = getChangingUserId();
-                final boolean isCurrentUser = (userId == mSettings.getUserId());
+                final boolean isCurrentUser = (userId == mCurrentUserId);
                 final AdditionalSubtypeMap additionalSubtypeMap =
                         AdditionalSubtypeMapRepository.get(userId);
                 final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
@@ -1135,8 +1136,6 @@
                 if (!isCurrentUser) {
                     return;
                 }
-                mSettings = queryInputMethodServicesInternal(mContext, userId,
-                        newAdditionalSubtypeMap, DirectBootAwareness.AUTO);
                 postInputMethodSettingUpdatedLocked(false /* resetDefaultEnabledIme */);
 
                 boolean changed = false;
@@ -1291,8 +1290,7 @@
     void onUnlockUser(@UserIdInt int userId) {
         synchronized (ImfLock.class) {
             if (DEBUG) {
-                Slog.d(TAG, "onUnlockUser: userId=" + userId + " curUserId="
-                        + mSettings.getUserId());
+                Slog.d(TAG, "onUnlockUser: userId=" + userId + " curUserId=" + mCurrentUserId);
             }
             if (!mSystemReady) {
                 return;
@@ -1300,8 +1298,7 @@
             final InputMethodSettings newSettings = queryInputMethodServicesInternal(mContext,
                     userId, AdditionalSubtypeMapRepository.get(userId), DirectBootAwareness.AUTO);
             InputMethodSettingsRepository.put(userId, newSettings);
-            if (mSettings.getUserId() == userId) {
-                mSettings = newSettings;
+            if (mCurrentUserId == userId) {
                 // We need to rebuild IMEs.
                 postInputMethodSettingUpdatedLocked(false /* resetDefaultEnabledIme */);
                 updateInputMethodsFromSettingsLocked(true /* enabledChanged */);
@@ -1375,16 +1372,16 @@
             InputMethodSettingsRepository.initialize(mHandler, mContext);
             AdditionalSubtypeMapRepository.initialize(mHandler, mContext);
 
-            final int userId = mActivityManagerInternal.getCurrentUserId();
+            mCurrentUserId = mActivityManagerInternal.getCurrentUserId();
 
-            mSettings = InputMethodSettingsRepository.get(userId);
+            final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
 
             mSwitchingController =
                     InputMethodSubtypeSwitchingController.createInstanceLocked(context,
-                            mSettings.getMethodMap(), userId);
+                            settings.getMethodMap(), settings.getUserId());
             mHardwareKeyboardShortcutController =
-                    new HardwareKeyboardShortcutController(mSettings.getMethodMap(),
-                            mSettings.getUserId());
+                    new HardwareKeyboardShortcutController(settings.getMethodMap(),
+                            settings.getUserId());
             mMenuController = new InputMethodMenuController(this);
             mBindingController =
                     bindingControllerForTesting != null
@@ -1416,7 +1413,7 @@
     @GuardedBy("ImfLock.class")
     @UserIdInt
     int getCurrentImeUserIdLocked() {
-        return mSettings.getUserId();
+        return mCurrentUserId;
     }
 
     private final class InkWindowInitializer implements Runnable {
@@ -1452,12 +1449,13 @@
     private void resetDefaultImeLocked(Context context) {
         // Do not reset the default (current) IME when it is a 3rd-party IME
         String selectedMethodId = getSelectedMethodIdLocked();
+        final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
         if (selectedMethodId != null
-                && !mSettings.getMethodMap().get(selectedMethodId).isSystem()) {
+                && !settings.getMethodMap().get(selectedMethodId).isSystem()) {
             return;
         }
         final List<InputMethodInfo> suitableImes = InputMethodInfoUtils.getDefaultEnabledImes(
-                context, mSettings.getEnabledInputMethodList());
+                context, settings.getEnabledInputMethodList());
         if (suitableImes.isEmpty()) {
             Slog.i(TAG, "No default found");
             return;
@@ -1513,7 +1511,7 @@
             IInputMethodClientInvoker clientToBeReset) {
         if (DEBUG) {
             Slog.d(TAG, "Switching user stage 1/3. newUserId=" + newUserId
-                    + " currentUserId=" + mSettings.getUserId());
+                    + " currentUserId=" + mCurrentUserId);
         }
 
         maybeInitImeNavbarConfigLocked(newUserId);
@@ -1521,8 +1519,9 @@
         // ContentObserver should be registered again when the user is changed
         mSettingsObserver.registerContentObserverLocked(newUserId);
 
-        mSettings = InputMethodSettings.createEmptyMap(newUserId);
-        final String defaultImiId = mSettings.getSelectedInputMethod();
+        mCurrentUserId = newUserId;
+        final String defaultImiId = SecureSettingsWrapper.getString(
+                Settings.Secure.DEFAULT_INPUT_METHOD, null, newUserId);
 
         if (DEBUG) {
             Slog.d(TAG, "Switching user stage 2/3. newUserId=" + newUserId
@@ -1540,12 +1539,9 @@
         // and user switch would not happen at that time.
         resetCurrentMethodAndClientLocked(UnbindReason.SWITCH_USER);
 
-        final InputMethodSettings newSettings = queryInputMethodServicesInternal(mContext,
-                newUserId, AdditionalSubtypeMapRepository.get(newUserId), DirectBootAwareness.AUTO);
-        InputMethodSettingsRepository.put(newUserId, newSettings);
-        mSettings = newSettings;
+        final InputMethodSettings newSettings = InputMethodSettingsRepository.get(newUserId);
         postInputMethodSettingUpdatedLocked(initialUserSwitch /* resetDefaultEnabledIme */);
-        if (TextUtils.isEmpty(mSettings.getSelectedInputMethod())) {
+        if (TextUtils.isEmpty(newSettings.getSelectedInputMethod())) {
             // This is the first time of the user switch and
             // set the current ime to the proper one.
             resetDefaultImeLocked(mContext);
@@ -1555,12 +1551,12 @@
         if (initialUserSwitch) {
             InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(
                     getPackageManagerForUser(mContext, newUserId),
-                    mSettings.getEnabledInputMethodList());
+                    newSettings.getEnabledInputMethodList());
         }
 
         if (DEBUG) {
             Slog.d(TAG, "Switching user stage 3/3. newUserId=" + newUserId
-                    + " selectedIme=" + mSettings.getSelectedInputMethod());
+                    + " selectedIme=" + newSettings.getSelectedInputMethod());
         }
 
         if (mIsInteractive && clientToBeReset != null) {
@@ -1583,7 +1579,7 @@
             }
             if (!mSystemReady) {
                 mSystemReady = true;
-                final int currentUserId = mSettings.getUserId();
+                final int currentUserId = mCurrentUserId;
                 mStatusBarManagerInternal =
                         LocalServices.getService(StatusBarManagerInternal.class);
                 hideStatusBarIconLocked();
@@ -1604,7 +1600,7 @@
                     // the "mImeDrawsImeNavBarResLazyInitFuture" field.
                     synchronized (ImfLock.class) {
                         mImeDrawsImeNavBarResLazyInitFuture = null;
-                        if (currentUserId != mSettings.getUserId()) {
+                        if (currentUserId != mCurrentUserId) {
                             // This means that the current user is already switched to other user
                             // before the background task is executed. In this scenario the relevant
                             // field should already be initialized.
@@ -1623,19 +1619,19 @@
                         UserHandle.ALL, broadcastFilterForAllUsers, null, null,
                         Context.RECEIVER_EXPORTED);
 
-                final String defaultImiId = mSettings.getSelectedInputMethod();
+                final String defaultImiId = SecureSettingsWrapper.getString(
+                        Settings.Secure.DEFAULT_INPUT_METHOD, null, currentUserId);
                 final boolean imeSelectedOnBoot = !TextUtils.isEmpty(defaultImiId);
                 final InputMethodSettings newSettings = queryInputMethodServicesInternal(mContext,
                         currentUserId, AdditionalSubtypeMapRepository.get(currentUserId),
                         DirectBootAwareness.AUTO);
                 InputMethodSettingsRepository.put(currentUserId, newSettings);
-                mSettings = newSettings;
                 postInputMethodSettingUpdatedLocked(
                         !imeSelectedOnBoot /* resetDefaultEnabledIme */);
                 updateFromSettingsLocked(true);
                 InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(
                         getPackageManagerForUser(mContext, currentUserId),
-                        mSettings.getEnabledInputMethodList());
+                        newSettings.getEnabledInputMethodList());
             }
         }
     }
@@ -1682,7 +1678,7 @@
         }
         synchronized (ImfLock.class) {
             final int[] resolvedUserIds = InputMethodUtils.resolveUserId(userId,
-                    mSettings.getUserId(), null);
+                    mCurrentUserId, null);
             if (resolvedUserIds.length != 1) {
                 return Collections.emptyList();
             }
@@ -1705,7 +1701,7 @@
         }
         synchronized (ImfLock.class) {
             final int[] resolvedUserIds = InputMethodUtils.resolveUserId(userId,
-                    mSettings.getUserId(), null);
+                    mCurrentUserId, null);
             if (resolvedUserIds.length != 1) {
                 return Collections.emptyList();
             }
@@ -1733,7 +1729,7 @@
             }
 
             // Check if selected IME of current user supports handwriting.
-            if (userId == mSettings.getUserId()) {
+            if (userId == mCurrentUserId) {
                 return mBindingController.supportsStylusHandwriting()
                         && (!connectionless
                                 || mBindingController.supportsConnectionlessStylusHandwriting());
@@ -1781,15 +1777,8 @@
     @GuardedBy("ImfLock.class")
     private List<InputMethodInfo> getEnabledInputMethodListLocked(@UserIdInt int userId,
             int callingUid) {
-        final ArrayList<InputMethodInfo> methodList;
-        final InputMethodSettings settings;
-        if (userId == mSettings.getUserId()) {
-            methodList = mSettings.getEnabledInputMethodList();
-            settings = mSettings;
-        } else {
-            settings = InputMethodSettingsRepository.get(userId);
-            methodList = settings.getEnabledInputMethodList();
-        }
+        final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
+        final ArrayList<InputMethodInfo> methodList = settings.getEnabledInputMethodList();
         // filter caller's access to input methods
         methodList.removeIf(imi ->
                 !canCallerAccessInputMethod(imi.getPackageName(), callingUid, userId, settings));
@@ -2028,7 +2017,7 @@
 
         final boolean restarting = !initial;
         final Binder startInputToken = new Binder();
-        final StartInputInfo info = new StartInputInfo(mSettings.getUserId(),
+        final StartInputInfo info = new StartInputInfo(mCurrentUserId,
                 getCurTokenLocked(),
                 mCurTokenDisplayId, getCurIdLocked(), startInputReason, restarting,
                 UserHandle.getUserId(mCurClient.mUid),
@@ -2042,9 +2031,9 @@
         // same-user scenarios.
         // That said ignoring cross-user scenario will never affect IMEs that do not have
         // INTERACT_ACROSS_USERS(_FULL) permissions, which is actually almost always the case.
-        if (mSettings.getUserId() == UserHandle.getUserId(
+        if (mCurrentUserId == UserHandle.getUserId(
                 mCurClient.mUid)) {
-            mPackageManagerInternal.grantImplicitAccess(mSettings.getUserId(),
+            mPackageManagerInternal.grantImplicitAccess(mCurrentUserId,
                     null /* intent */, UserHandle.getAppId(getCurMethodUidLocked()),
                     mCurClient.mUid, true /* direct */);
         }
@@ -2067,7 +2056,8 @@
         }
 
         String curId = getCurIdLocked();
-        final InputMethodInfo curInputMethodInfo = mSettings.getMethodMap().get(curId);
+        final InputMethodInfo curInputMethodInfo = InputMethodSettingsRepository.get(mCurrentUserId)
+                .getMethodMap().get(curId);
         final boolean suppressesSpellChecker =
                 curInputMethodInfo != null && curInputMethodInfo.suppressesSpellChecker();
         final SparseArray<IAccessibilityInputMethodSession> accessibilityInputMethodSessions =
@@ -2255,17 +2245,18 @@
             return currentMethodId;
         }
 
+        final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
         final int oldDeviceId = mDeviceIdToShowIme;
         mDeviceIdToShowIme = mVdmInternal.getDeviceIdForDisplayId(mDisplayIdToShowIme);
         if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) {
             if (oldDeviceId == DEVICE_ID_DEFAULT) {
                 return currentMethodId;
             }
-            final String defaultDeviceMethodId = mSettings.getSelectedDefaultDeviceInputMethod();
+            final String defaultDeviceMethodId = settings.getSelectedDefaultDeviceInputMethod();
             if (DEBUG) {
                 Slog.v(TAG, "Restoring default device input method: " + defaultDeviceMethodId);
             }
-            mSettings.putSelectedDefaultDeviceInputMethod(null);
+            settings.putSelectedDefaultDeviceInputMethod(null);
             return defaultDeviceMethodId;
         }
 
@@ -2273,7 +2264,7 @@
                 mVirtualDeviceMethodMap.get(mDeviceIdToShowIme, currentMethodId);
         if (Objects.equals(deviceMethodId, currentMethodId)) {
             return currentMethodId;
-        } else if (!mSettings.getMethodMap().containsKey(deviceMethodId)) {
+        } else if (!settings.getMethodMap().containsKey(deviceMethodId)) {
             if (DEBUG) {
                 Slog.v(TAG, "Disabling IME on virtual device with id " + mDeviceIdToShowIme
                         + " because its custom input method is not available: " + deviceMethodId);
@@ -2285,7 +2276,7 @@
             if (DEBUG) {
                 Slog.v(TAG, "Storing default device input method " + currentMethodId);
             }
-            mSettings.putSelectedDefaultDeviceInputMethod(currentMethodId);
+            settings.putSelectedDefaultDeviceInputMethod(currentMethodId);
         }
         if (DEBUG) {
             Slog.v(TAG, "Switching current input method from " + currentMethodId
@@ -2315,7 +2306,8 @@
         if (isSoftInputModeStateVisibleAllowed(unverifiedTargetSdkVersion, startInputFlags)) {
             return false;
         }
-        final InputMethodInfo imi = mSettings.getMethodMap().get(selectedMethodId);
+        final InputMethodInfo imi = InputMethodSettingsRepository.get(mCurrentUserId)
+                .getMethodMap().get(selectedMethodId);
         if (imi == null) {
             return false;
         }
@@ -2659,7 +2651,7 @@
                 } else if (packageName != null) {
                     if (DEBUG) Slog.d(TAG, "show a small icon for the input method");
                     final PackageManager userAwarePackageManager =
-                            getPackageManagerForUser(mContext, mSettings.getUserId());
+                            getPackageManagerForUser(mContext, mCurrentUserId);
                     ApplicationInfo applicationInfo = null;
                     try {
                         applicationInfo = userAwarePackageManager.getApplicationInfo(packageName,
@@ -2721,7 +2713,7 @@
             return false;
         }
         if (mWindowManagerInternal.isKeyguardShowingAndNotOccluded()
-                && mWindowManagerInternal.isKeyguardSecure(mSettings.getUserId())) {
+                && mWindowManagerInternal.isKeyguardSecure(mCurrentUserId)) {
             return false;
         }
         if ((visibility & InputMethodService.IME_ACTIVE) == 0
@@ -2738,7 +2730,8 @@
             return false;
         }
 
-        List<InputMethodInfo> imes = mSettings.getEnabledInputMethodListWithFilter(
+        final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+        List<InputMethodInfo> imes = settings.getEnabledInputMethodListWithFilter(
                 InputMethodInfo::shouldShowInInputMethodPicker);
         final int numImes = imes.size();
         if (numImes > 2) return true;
@@ -2750,7 +2743,7 @@
         for (int i = 0; i < numImes; ++i) {
             final InputMethodInfo imi = imes.get(i);
             final List<InputMethodSubtype> subtypes =
-                    mSettings.getEnabledInputMethodSubtypeList(imi, true);
+                    settings.getEnabledInputMethodSubtypeList(imi, true);
             final int subtypeCount = subtypes.size();
             if (subtypeCount == 0) {
                 ++nonAuxCount;
@@ -2902,11 +2895,12 @@
 
     @GuardedBy("ImfLock.class")
     void updateInputMethodsFromSettingsLocked(boolean enabledMayChange) {
+        final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
         if (enabledMayChange) {
             final PackageManager userAwarePackageManager = getPackageManagerForUser(mContext,
-                    mSettings.getUserId());
+                    settings.getUserId());
 
-            List<InputMethodInfo> enabled = mSettings.getEnabledInputMethodList();
+            List<InputMethodInfo> enabled = settings.getEnabledInputMethodList();
             for (int i = 0; i < enabled.size(); i++) {
                 // We allow the user to select "disabled until used" apps, so if they
                 // are enabling one of those here we now need to make it enabled.
@@ -2933,20 +2927,20 @@
 
         if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) {
             String ime = SecureSettingsWrapper.getString(
-                    Settings.Secure.DEFAULT_INPUT_METHOD, null, mSettings.getUserId());
+                    Settings.Secure.DEFAULT_INPUT_METHOD, null, settings.getUserId());
             String defaultDeviceIme = SecureSettingsWrapper.getString(
-                    Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null, mSettings.getUserId());
+                    Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null, settings.getUserId());
             if (defaultDeviceIme != null && !Objects.equals(ime, defaultDeviceIme)) {
                 if (DEBUG) {
                     Slog.v(TAG, "Current input method " + ime + " differs from the stored default"
-                            + " device input method for user " + mSettings.getUserId()
+                            + " device input method for user " + settings.getUserId()
                             + " - restoring " + defaultDeviceIme);
                 }
                 SecureSettingsWrapper.putString(
                         Settings.Secure.DEFAULT_INPUT_METHOD, defaultDeviceIme,
-                        mSettings.getUserId());
+                        settings.getUserId());
                 SecureSettingsWrapper.putString(
-                        Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null, mSettings.getUserId());
+                        Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null, settings.getUserId());
             }
         }
 
@@ -2954,14 +2948,14 @@
         // ENABLED_INPUT_METHODS is taking care of keeping them correctly in
         // sync, so we will never have a DEFAULT_INPUT_METHOD that is not
         // enabled.
-        String id = mSettings.getSelectedInputMethod();
+        String id = settings.getSelectedInputMethod();
         // There is no input method selected, try to choose new applicable input method.
         if (TextUtils.isEmpty(id) && chooseNewDefaultIMELocked()) {
-            id = mSettings.getSelectedInputMethod();
+            id = settings.getSelectedInputMethod();
         }
         if (!TextUtils.isEmpty(id)) {
             try {
-                setInputMethodLocked(id, mSettings.getSelectedInputMethodSubtypeId(id));
+                setInputMethodLocked(id, settings.getSelectedInputMethodSubtypeId(id));
             } catch (IllegalArgumentException e) {
                 Slog.w(TAG, "Unknown input method from prefs: " + id, e);
                 resetCurrentMethodAndClientLocked(UnbindReason.SWITCH_IME_FAILED);
@@ -2972,18 +2966,18 @@
         }
 
         // TODO: Instantiate mSwitchingController for each user.
-        if (mSettings.getUserId() == mSwitchingController.getUserId()) {
-            mSwitchingController.resetCircularListLocked(mSettings.getMethodMap());
+        if (settings.getUserId() == mSwitchingController.getUserId()) {
+            mSwitchingController.resetCircularListLocked(settings.getMethodMap());
         } else {
             mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked(
-                    mContext, mSettings.getMethodMap(), mSettings.getUserId());
+                    mContext, settings.getMethodMap(), settings.getUserId());
         }
         // TODO: Instantiate mHardwareKeyboardShortcutController for each user.
-        if (mSettings.getUserId() == mHardwareKeyboardShortcutController.getUserId()) {
-            mHardwareKeyboardShortcutController.reset(mSettings.getMethodMap());
+        if (settings.getUserId() == mHardwareKeyboardShortcutController.getUserId()) {
+            mHardwareKeyboardShortcutController.reset(settings.getMethodMap());
         } else {
             mHardwareKeyboardShortcutController = new HardwareKeyboardShortcutController(
-                    mSettings.getMethodMap(), mSettings.getUserId());
+                    settings.getMethodMap(), settings.getUserId());
         }
         sendOnNavButtonFlagsChangedLocked();
     }
@@ -3007,14 +3001,15 @@
 
     @GuardedBy("ImfLock.class")
     void setInputMethodLocked(String id, int subtypeId, int deviceId) {
-        InputMethodInfo info = mSettings.getMethodMap().get(id);
+        final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+        InputMethodInfo info = settings.getMethodMap().get(id);
         if (info == null) {
             throw getExceptionForUnknownImeId(id);
         }
 
         // See if we need to notify a subtype change within the same IME.
         if (id.equals(getSelectedMethodIdLocked())) {
-            final int userId = mSettings.getUserId();
+            final int userId = settings.getUserId();
             final int subtypeCount = info.getSubtypeCount();
             if (subtypeCount <= 0) {
                 notifyInputMethodSubtypeChangedLocked(userId, info, null);
@@ -3055,7 +3050,7 @@
             // method is a custom one specific to a virtual device. So only update the settings
             // entry used to restore the default device input method once we want to show the IME
             // back on the default device.
-            mSettings.putSelectedDefaultDeviceInputMethod(id);
+            settings.putSelectedDefaultDeviceInputMethod(id);
             return;
         }
         IInputMethodInvoker curMethod = getCurMethodLocked();
@@ -3583,7 +3578,7 @@
                             return InputBindResult.USER_SWITCHING;
                         }
                         final int[] profileIdsWithDisabled = mUserManagerInternal.getProfileIds(
-                                mSettings.getUserId(), false /* enabledOnly */);
+                                mCurrentUserId, false /* enabledOnly */);
                         for (int profileId : profileIdsWithDisabled) {
                             if (profileId == userId) {
                                 scheduleSwitchUserTaskLocked(userId, cs.mClient);
@@ -3629,10 +3624,10 @@
                     }
 
                     // Verify if caller is a background user.
-                    final int currentUserId = mSettings.getUserId();
-                    if (userId != currentUserId) {
+                    if (userId != mCurrentUserId) {
                         if (ArrayUtils.contains(
-                                mUserManagerInternal.getProfileIds(currentUserId, false), userId)) {
+                                mUserManagerInternal.getProfileIds(mCurrentUserId, false),
+                                userId)) {
                             // cross-profile access is always allowed here to allow
                             // profile-switching.
                             scheduleSwitchUserTaskLocked(userId, cs.mClient);
@@ -3821,7 +3816,7 @@
                 && mImeBindingState.mFocusedWindowClient.mClient.asBinder() == client.asBinder()) {
             return true;
         }
-        if (mSettings.getUserId() != UserHandle.getUserId(uid)) {
+        if (mCurrentUserId != UserHandle.getUserId(uid)) {
             return false;
         }
         if (getCurIntentLocked() != null && InputMethodUtils.checkIfPackageBelongsToUid(
@@ -3885,9 +3880,10 @@
             if (!calledWithValidTokenLocked(token)) {
                 return;
             }
-            final InputMethodInfo imi = mSettings.getMethodMap().get(id);
+            final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+            final InputMethodInfo imi = settings.getMethodMap().get(id);
             if (imi == null || !canCallerAccessInputMethod(
-                    imi.getPackageName(), callingUid, userId, mSettings)) {
+                    imi.getPackageName(), callingUid, userId, settings)) {
                 throw getExceptionForUnknownImeId(id);
             }
             setInputMethodWithSubtypeIdLocked(token, id, NOT_A_SUBTYPE_ID);
@@ -3903,9 +3899,10 @@
             if (!calledWithValidTokenLocked(token)) {
                 return;
             }
-            final InputMethodInfo imi = mSettings.getMethodMap().get(id);
+            final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+            final InputMethodInfo imi = settings.getMethodMap().get(id);
             if (imi == null || !canCallerAccessInputMethod(
-                    imi.getPackageName(), callingUid, userId, mSettings)) {
+                    imi.getPackageName(), callingUid, userId, settings)) {
                 throw getExceptionForUnknownImeId(id);
             }
             if (subtype != null) {
@@ -3923,10 +3920,11 @@
             if (!calledWithValidTokenLocked(token)) {
                 return false;
             }
-            final Pair<String, String> lastIme = mSettings.getLastInputMethodAndSubtype();
+            final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+            final Pair<String, String> lastIme = settings.getLastInputMethodAndSubtype();
             final InputMethodInfo lastImi;
             if (lastIme != null) {
-                lastImi = mSettings.getMethodMap().get(lastIme.first);
+                lastImi = settings.getMethodMap().get(lastIme.first);
             } else {
                 lastImi = null;
             }
@@ -3950,7 +3948,7 @@
                 // This is a safety net. If the currentSubtype can't be added to the history
                 // and the framework couldn't find the last ime, we will make the last ime be
                 // the most applicable enabled keyboard subtype of the system imes.
-                final List<InputMethodInfo> enabled = mSettings.getEnabledInputMethodList();
+                final List<InputMethodInfo> enabled = settings.getEnabledInputMethodList();
                 if (enabled != null) {
                     final int enabledCount = enabled.size();
                     final String locale;
@@ -3958,7 +3956,7 @@
                             && !TextUtils.isEmpty(mCurrentSubtype.getLocale())) {
                         locale = mCurrentSubtype.getLocale();
                     } else {
-                        locale = SystemLocaleWrapper.get(mSettings.getUserId()).get(0).toString();
+                        locale = SystemLocaleWrapper.get(mCurrentUserId).get(0).toString();
                     }
                     for (int i = 0; i < enabledCount; ++i) {
                         final InputMethodInfo imi = enabled.get(i);
@@ -4005,8 +4003,9 @@
 
     @GuardedBy("ImfLock.class")
     private boolean switchToNextInputMethodLocked(@Nullable IBinder token, boolean onlyCurrentIme) {
+        final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
         final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
-                onlyCurrentIme, mSettings.getMethodMap().get(getSelectedMethodIdLocked()),
+                onlyCurrentIme, settings.getMethodMap().get(getSelectedMethodIdLocked()),
                 mCurrentSubtype);
         if (nextSubtype == null) {
             return false;
@@ -4022,9 +4021,10 @@
             if (!calledWithValidTokenLocked(token)) {
                 return false;
             }
+            final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
             final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
                     false /* onlyCurrentIme */,
-                    mSettings.getMethodMap().get(getSelectedMethodIdLocked()), mCurrentSubtype);
+                    settings.getMethodMap().get(getSelectedMethodIdLocked()), mCurrentSubtype);
             return nextSubtype != null;
         }
     }
@@ -4036,10 +4036,6 @@
                     Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
         }
         synchronized (ImfLock.class) {
-            if (mSettings.getUserId() == userId) {
-                return mSettings.getLastInputMethodSubtype();
-            }
-
             return InputMethodSettingsRepository.get(userId).getLastInputMethodSubtype();
         }
     }
@@ -4071,7 +4067,7 @@
             }
 
             final var additionalSubtypeMap = AdditionalSubtypeMapRepository.get(userId);
-            final boolean isCurrentUser = (mSettings.getUserId() == userId);
+            final boolean isCurrentUser = (mCurrentUserId == userId);
             final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
             final var newAdditionalSubtypeMap = settings.getNewAdditionalSubtypeMap(
                     imiId, toBeAdded, additionalSubtypeMap, mPackageManagerInternal, callingUid);
@@ -4085,7 +4081,6 @@
                 if (isCurrentUser) {
                     final long ident = Binder.clearCallingIdentity();
                     try {
-                        mSettings = newSettings;
                         postInputMethodSettingUpdatedLocked(false /* resetDefaultEnabledIme */);
                     } finally {
                         Binder.restoreCallingIdentity(ident);
@@ -4115,7 +4110,7 @@
         final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (ImfLock.class) {
-                final boolean currentUser = (mSettings.getUserId() == userId);
+                final boolean currentUser = (mCurrentUserId == userId);
                 final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
                 if (!settings.setEnabledInputMethodSubtypes(imeId, subtypeHashCodes)) {
                     return;
@@ -4458,11 +4453,11 @@
                 }
                 return;
             }
-            if (mSettings.getUserId() != mSwitchingController.getUserId()) {
+            if (mCurrentUserId != mSwitchingController.getUserId()) {
                 return;
             }
-            final InputMethodInfo imi =
-                    mSettings.getMethodMap().get(getSelectedMethodIdLocked());
+            final InputMethodInfo imi = InputMethodSettingsRepository.get(mCurrentUserId)
+                    .getMethodMap().get(getSelectedMethodIdLocked());
             if (imi != null) {
                 mSwitchingController.onUserActionLocked(imi, mCurrentSubtype);
             }
@@ -4522,8 +4517,9 @@
             return;
         } else {
             // Called with current IME's token.
-            if (mSettings.getMethodMap().get(id) != null
-                    && mSettings.getEnabledInputMethodListWithFilter(
+            final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+            if (settings.getMethodMap().get(id) != null
+                    && settings.getEnabledInputMethodListWithFilter(
                             (info) -> info.getId().equals(id)).isEmpty()) {
                 throw new IllegalStateException("Requested IME is not enabled: " + id);
             }
@@ -4702,21 +4698,23 @@
                         return false;
                 }
                 synchronized (ImfLock.class) {
+                    final InputMethodSettings settings =
+                            InputMethodSettingsRepository.get(mCurrentUserId);
                     final boolean isScreenLocked = mWindowManagerInternal.isKeyguardLocked()
-                            && mWindowManagerInternal.isKeyguardSecure(mSettings.getUserId());
-                    final String lastInputMethodId = mSettings.getSelectedInputMethod();
+                            && mWindowManagerInternal.isKeyguardSecure(settings.getUserId());
+                    final String lastInputMethodId = settings.getSelectedInputMethod();
                     int lastInputMethodSubtypeId =
-                            mSettings.getSelectedInputMethodSubtypeId(lastInputMethodId);
+                            settings.getSelectedInputMethodSubtypeId(lastInputMethodId);
 
                     final List<ImeSubtypeListItem> imList = InputMethodSubtypeSwitchingController
                             .getSortedInputMethodAndSubtypeList(
                                     showAuxSubtypes, isScreenLocked, true /* forImeMenu */,
-                                    mContext, mSettings.getMethodMap(), mSettings.getUserId());
+                                    mContext, settings.getMethodMap(), settings.getUserId());
                     if (imList.isEmpty()) {
                         Slog.w(TAG, "Show switching menu failed, imList is empty,"
                                 + " showAuxSubtypes: " + showAuxSubtypes
                                 + " isScreenLocked: " + isScreenLocked
-                                + " userId: " + mSettings.getUserId());
+                                + " userId: " + settings.getUserId());
                         return false;
                     }
 
@@ -4902,8 +4900,9 @@
 
     @GuardedBy("ImfLock.class")
     private boolean chooseNewDefaultIMELocked() {
+        final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
         final InputMethodInfo imi = InputMethodInfoUtils.getMostApplicableDefaultIME(
-                mSettings.getEnabledInputMethodList());
+                settings.getEnabledInputMethodList());
         if (imi != null) {
             if (DEBUG) {
                 Slog.d(TAG, "New default IME was selected: " + imi.getId());
@@ -5017,6 +5016,8 @@
         mMethodMapUpdateCount++;
         mMyPackageMonitor.clearKnownImePackageNamesLocked();
 
+        final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+
         // Construct the set of possible IME packages for onPackageChanged() to avoid false
         // negatives when the package state remains to be the same but only the component state is
         // changed.
@@ -5027,7 +5028,7 @@
             final List<ResolveInfo> allInputMethodServices =
                     mContext.getPackageManager().queryIntentServicesAsUser(
                             new Intent(InputMethod.SERVICE_INTERFACE),
-                            PackageManager.MATCH_DISABLED_COMPONENTS, mSettings.getUserId());
+                            PackageManager.MATCH_DISABLED_COMPONENTS, settings.getUserId());
             final int numImes = allInputMethodServices.size();
             for (int i = 0; i < numImes; ++i) {
                 final ServiceInfo si = allInputMethodServices.get(i).serviceInfo;
@@ -5042,11 +5043,11 @@
         if (!resetDefaultEnabledIme) {
             boolean enabledImeFound = false;
             boolean enabledNonAuxImeFound = false;
-            final List<InputMethodInfo> enabledImes = mSettings.getEnabledInputMethodList();
+            final List<InputMethodInfo> enabledImes = settings.getEnabledInputMethodList();
             final int numImes = enabledImes.size();
             for (int i = 0; i < numImes; ++i) {
                 final InputMethodInfo imi = enabledImes.get(i);
-                if (mSettings.getMethodMap().containsKey(imi.getId())) {
+                if (settings.getMethodMap().containsKey(imi.getId())) {
                     enabledImeFound = true;
                     if (!imi.isAuxiliaryIme()) {
                         enabledNonAuxImeFound = true;
@@ -5070,7 +5071,7 @@
 
         if (resetDefaultEnabledIme || reenableMinimumNonAuxSystemImes) {
             final ArrayList<InputMethodInfo> defaultEnabledIme =
-                    InputMethodInfoUtils.getDefaultEnabledImes(mContext, mSettings.getMethodList(),
+                    InputMethodInfoUtils.getDefaultEnabledImes(mContext, settings.getMethodList(),
                             reenableMinimumNonAuxSystemImes);
             final int numImes = defaultEnabledIme.size();
             for (int i = 0; i < numImes; ++i) {
@@ -5082,9 +5083,9 @@
             }
         }
 
-        final String defaultImiId = mSettings.getSelectedInputMethod();
+        final String defaultImiId = settings.getSelectedInputMethod();
         if (!TextUtils.isEmpty(defaultImiId)) {
-            if (!mSettings.getMethodMap().containsKey(defaultImiId)) {
+            if (!settings.getMethodMap().containsKey(defaultImiId)) {
                 Slog.w(TAG, "Default IME is uninstalled. Choose new default IME.");
                 if (chooseNewDefaultIMELocked()) {
                     updateInputMethodsFromSettingsLocked(true);
@@ -5098,26 +5099,26 @@
         updateDefaultVoiceImeIfNeededLocked();
 
         // TODO: Instantiate mSwitchingController for each user.
-        if (mSettings.getUserId() == mSwitchingController.getUserId()) {
-            mSwitchingController.resetCircularListLocked(mSettings.getMethodMap());
+        if (settings.getUserId() == mSwitchingController.getUserId()) {
+            mSwitchingController.resetCircularListLocked(settings.getMethodMap());
         } else {
             mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked(
-                    mContext, mSettings.getMethodMap(), mSettings.getUserId());
+                    mContext, settings.getMethodMap(), mCurrentUserId);
         }
         // TODO: Instantiate mHardwareKeyboardShortcutController for each user.
-        if (mSettings.getUserId() == mHardwareKeyboardShortcutController.getUserId()) {
-            mHardwareKeyboardShortcutController.reset(mSettings.getMethodMap());
+        if (settings.getUserId() == mHardwareKeyboardShortcutController.getUserId()) {
+            mHardwareKeyboardShortcutController.reset(settings.getMethodMap());
         } else {
             mHardwareKeyboardShortcutController = new HardwareKeyboardShortcutController(
-                    mSettings.getMethodMap(), mSettings.getUserId());
+                    settings.getMethodMap(), settings.getUserId());
         }
 
         sendOnNavButtonFlagsChangedLocked();
 
         // Notify InputMethodListListeners of the new installed InputMethods.
-        final List<InputMethodInfo> inputMethodList = mSettings.getMethodList();
+        final List<InputMethodInfo> inputMethodList = settings.getMethodList();
         mHandler.obtainMessage(MSG_DISPATCH_ON_INPUT_METHOD_LIST_UPDATED,
-                mSettings.getUserId(), 0 /* unused */, inputMethodList).sendToTarget();
+                settings.getUserId(), 0 /* unused */, inputMethodList).sendToTarget();
     }
 
     @GuardedBy("ImfLock.class")
@@ -5132,11 +5133,12 @@
 
     @GuardedBy("ImfLock.class")
     private void updateDefaultVoiceImeIfNeededLocked() {
+        final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
         final String systemSpeechRecognizer =
                 mContext.getString(com.android.internal.R.string.config_systemSpeechRecognizer);
-        final String currentDefaultVoiceImeId = mSettings.getDefaultVoiceInputMethod();
+        final String currentDefaultVoiceImeId = settings.getDefaultVoiceInputMethod();
         final InputMethodInfo newSystemVoiceIme = InputMethodInfoUtils.chooseSystemVoiceIme(
-                mSettings.getMethodMap(), systemSpeechRecognizer, currentDefaultVoiceImeId);
+                settings.getMethodMap(), systemSpeechRecognizer, currentDefaultVoiceImeId);
         if (newSystemVoiceIme == null) {
             if (DEBUG) {
                 Slog.i(TAG, "Found no valid default Voice IME. If the user is still locked,"
@@ -5145,7 +5147,7 @@
             // Clear DEFAULT_VOICE_INPUT_METHOD when necessary.  Note that InputMethodSettings
             // does not update the actual Secure Settings until the user is unlocked.
             if (!TextUtils.isEmpty(currentDefaultVoiceImeId)) {
-                mSettings.putDefaultVoiceInputMethod("");
+                settings.putDefaultVoiceInputMethod("");
                 // We don't support disabling the voice ime when a package is removed from the
                 // config.
             }
@@ -5158,7 +5160,7 @@
             Slog.i(TAG, "Enabling the default Voice IME:" + newSystemVoiceIme);
         }
         setInputMethodEnabledLocked(newSystemVoiceIme.getId(), true);
-        mSettings.putDefaultVoiceInputMethod(newSystemVoiceIme.getId());
+        settings.putDefaultVoiceInputMethod(newSystemVoiceIme.getId());
     }
 
     // ----------------------------------------------------------------------
@@ -5173,8 +5175,9 @@
      */
     @GuardedBy("ImfLock.class")
     private boolean setInputMethodEnabledLocked(String id, boolean enabled) {
+        final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
         if (enabled) {
-            final String enabledImeIdsStr = mSettings.getEnabledInputMethodsStr();
+            final String enabledImeIdsStr = settings.getEnabledInputMethodsStr();
             final String newEnabledImeIdsStr = InputMethodUtils.concatEnabledImeIds(
                     enabledImeIdsStr, id);
             if (TextUtils.equals(enabledImeIdsStr, newEnabledImeIdsStr)) {
@@ -5182,29 +5185,29 @@
                 // Nothing to do. The previous state was enabled.
                 return true;
             }
-            mSettings.putEnabledInputMethodsStr(newEnabledImeIdsStr);
+            settings.putEnabledInputMethodsStr(newEnabledImeIdsStr);
             // Previous state was disabled.
             return false;
         } else {
-            final List<Pair<String, ArrayList<String>>> enabledInputMethodsList = mSettings
+            final List<Pair<String, ArrayList<String>>> enabledInputMethodsList = settings
                     .getEnabledInputMethodsAndSubtypeList();
             StringBuilder builder = new StringBuilder();
-            if (mSettings.buildAndPutEnabledInputMethodsStrRemovingId(
+            if (settings.buildAndPutEnabledInputMethodsStrRemovingId(
                     builder, enabledInputMethodsList, id)) {
                 if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) {
                     // Disabled input method is currently selected, switch to another one.
-                    final String selId = mSettings.getSelectedInputMethod();
+                    final String selId = settings.getSelectedInputMethod();
                     if (id.equals(selId) && !chooseNewDefaultIMELocked()) {
                         Slog.i(TAG, "Can't find new IME, unsetting the current input method.");
                         resetSelectedInputMethodAndSubtypeLocked("");
                     }
-                } else if (id.equals(mSettings.getSelectedDefaultDeviceInputMethod())) {
+                } else if (id.equals(settings.getSelectedDefaultDeviceInputMethod())) {
                     // Disabled default device IME while using a virtual device one, choose a
                     // new default one but only update the settings.
                     InputMethodInfo newDefaultIme =
                             InputMethodInfoUtils.getMostApplicableDefaultIME(
-                                        mSettings.getEnabledInputMethodList());
-                    mSettings.putSelectedDefaultDeviceInputMethod(
+                                    settings.getEnabledInputMethodList());
+                    settings.putSelectedDefaultDeviceInputMethod(
                             newDefaultIme == null ? null : newDefaultIme.getId());
                 }
                 // Previous state was enabled.
@@ -5220,29 +5223,30 @@
     @GuardedBy("ImfLock.class")
     private void setSelectedInputMethodAndSubtypeLocked(InputMethodInfo imi, int subtypeId,
             boolean setSubtypeOnly) {
-        mSettings.saveCurrentInputMethodAndSubtypeToHistory(getSelectedMethodIdLocked(),
+        final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+        settings.saveCurrentInputMethodAndSubtypeToHistory(getSelectedMethodIdLocked(),
                 mCurrentSubtype);
 
         // Set Subtype here
         if (imi == null || subtypeId < 0) {
-            mSettings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
+            settings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
             mCurrentSubtype = null;
         } else {
             if (subtypeId < imi.getSubtypeCount()) {
                 InputMethodSubtype subtype = imi.getSubtypeAt(subtypeId);
-                mSettings.putSelectedSubtype(subtype.hashCode());
+                settings.putSelectedSubtype(subtype.hashCode());
                 mCurrentSubtype = subtype;
             } else {
-                mSettings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
+                settings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
                 // If the subtype is not specified, choose the most applicable one
                 mCurrentSubtype = getCurrentInputMethodSubtypeLocked();
             }
         }
-        notifyInputMethodSubtypeChangedLocked(mSettings.getUserId(), imi, mCurrentSubtype);
+        notifyInputMethodSubtypeChangedLocked(settings.getUserId(), imi, mCurrentSubtype);
 
         if (!setSubtypeOnly) {
             // Set InputMethod here
-            mSettings.putSelectedInputMethod(imi != null ? imi.getId() : "");
+            settings.putSelectedInputMethod(imi != null ? imi.getId() : "");
         }
     }
 
@@ -5250,13 +5254,15 @@
     private void resetSelectedInputMethodAndSubtypeLocked(String newDefaultIme) {
         mDeviceIdToShowIme = DEVICE_ID_DEFAULT;
         mDisplayIdToShowIme = INVALID_DISPLAY;
-        mSettings.putSelectedDefaultDeviceInputMethod(null);
 
-        InputMethodInfo imi = mSettings.getMethodMap().get(newDefaultIme);
+        final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+        settings.putSelectedDefaultDeviceInputMethod(null);
+
+        InputMethodInfo imi = settings.getMethodMap().get(newDefaultIme);
         int lastSubtypeId = NOT_A_SUBTYPE_ID;
         // newDefaultIme is empty when there is no candidate for the selected IME.
         if (imi != null && !TextUtils.isEmpty(newDefaultIme)) {
-            String subtypeHashCode = mSettings.getLastSubtypeForInputMethod(newDefaultIme);
+            String subtypeHashCode = settings.getLastSubtypeForInputMethod(newDefaultIme);
             if (subtypeHashCode != null) {
                 try {
                     lastSubtypeId = SubtypeUtils.getSubtypeIdFromHashCode(imi,
@@ -5283,7 +5289,7 @@
                     Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
         }
         synchronized (ImfLock.class) {
-            if (mSettings.getUserId() == userId) {
+            if (mCurrentUserId == userId) {
                 return getCurrentInputMethodSubtypeLocked();
             }
 
@@ -5308,26 +5314,27 @@
         if (selectedMethodId == null) {
             return null;
         }
-        final boolean subtypeIsSelected = mSettings.isSubtypeSelected();
-        final InputMethodInfo imi = mSettings.getMethodMap().get(selectedMethodId);
+        final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+        final boolean subtypeIsSelected = settings.isSubtypeSelected();
+        final InputMethodInfo imi = settings.getMethodMap().get(selectedMethodId);
         if (imi == null || imi.getSubtypeCount() == 0) {
             return null;
         }
         if (!subtypeIsSelected || mCurrentSubtype == null
                 || !SubtypeUtils.isValidSubtypeId(imi, mCurrentSubtype.hashCode())) {
-            int subtypeId = mSettings.getSelectedInputMethodSubtypeId(selectedMethodId);
+            int subtypeId = settings.getSelectedInputMethodSubtypeId(selectedMethodId);
             if (subtypeId == NOT_A_SUBTYPE_ID) {
                 // If there are no selected subtypes, the framework will try to find
                 // the most applicable subtype from explicitly or implicitly enabled
                 // subtypes.
                 List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypes =
-                        mSettings.getEnabledInputMethodSubtypeList(imi, true);
+                        settings.getEnabledInputMethodSubtypeList(imi, true);
                 // If there is only one explicitly or implicitly enabled subtype,
                 // just returns it.
                 if (explicitlyOrImplicitlyEnabledSubtypes.size() == 1) {
                     mCurrentSubtype = explicitlyOrImplicitlyEnabledSubtypes.get(0);
                 } else if (explicitlyOrImplicitlyEnabledSubtypes.size() > 1) {
-                    final String locale = SystemLocaleWrapper.get(mSettings.getUserId())
+                    final String locale = SystemLocaleWrapper.get(settings.getUserId())
                             .get(0).toString();
                     mCurrentSubtype = SubtypeUtils.findLastResortApplicableSubtype(
                             explicitlyOrImplicitlyEnabledSubtypes,
@@ -5356,16 +5363,16 @@
 
     @GuardedBy("ImfLock.class")
     private boolean switchToInputMethodLocked(String imeId, @UserIdInt int userId) {
-        if (userId == mSettings.getUserId()) {
-            if (!mSettings.getMethodMap().containsKey(imeId)
-                    || !mSettings.getEnabledInputMethodList()
-                    .contains(mSettings.getMethodMap().get(imeId))) {
+        final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
+        if (userId == mCurrentUserId) {
+            if (!settings.getMethodMap().containsKey(imeId)
+                    || !settings.getEnabledInputMethodList()
+                    .contains(settings.getMethodMap().get(imeId))) {
                 return false; // IME is not found or not enabled.
             }
             setInputMethodLocked(imeId, NOT_A_SUBTYPE_ID);
             return true;
         }
-        final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
         if (!settings.getMethodMap().containsKey(imeId)
                 || !settings.getEnabledInputMethodList().contains(
                         settings.getMethodMap().get(imeId))) {
@@ -5406,8 +5413,9 @@
 
     @GuardedBy("ImfLock.class")
     private void switchKeyboardLayoutLocked(int direction) {
-        final InputMethodInfo currentImi = mSettings.getMethodMap().get(
-                getSelectedMethodIdLocked());
+        final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+
+        final InputMethodInfo currentImi = settings.getMethodMap().get(getSelectedMethodIdLocked());
         if (currentImi == null) {
             return;
         }
@@ -5419,7 +5427,7 @@
         if (nextSubtypeHandle == null) {
             return;
         }
-        final InputMethodInfo nextImi = mSettings.getMethodMap().get(nextSubtypeHandle.getImeId());
+        final InputMethodInfo nextImi = settings.getMethodMap().get(nextSubtypeHandle.getImeId());
         if (nextImi == null) {
             return;
         }
@@ -5498,17 +5506,14 @@
         @Override
         public boolean setInputMethodEnabled(String imeId, boolean enabled, @UserIdInt int userId) {
             synchronized (ImfLock.class) {
-                if (userId == mSettings.getUserId()) {
-                    if (!mSettings.getMethodMap().containsKey(imeId)) {
-                        return false; // IME is not found.
-                    }
-                    setInputMethodEnabledLocked(imeId, enabled);
-                    return true;
-                }
                 final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
                 if (!settings.getMethodMap().containsKey(imeId)) {
                     return false; // IME is not found.
                 }
+                if (userId == mCurrentUserId) {
+                    setInputMethodEnabledLocked(imeId, enabled);
+                    return true;
+                }
                 if (enabled) {
                     final String enabledImeIdsStr = settings.getEnabledInputMethodsStr();
                     final String newEnabledImeIdsStr = InputMethodUtils.concatEnabledImeIds(
@@ -5835,8 +5840,9 @@
         final Printer p = new PrintWriterPrinter(pw);
 
         synchronized (ImfLock.class) {
+            final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
             p.println("Current Input Method Manager state:");
-            final List<InputMethodInfo> methodList = mSettings.getMethodList();
+            final List<InputMethodInfo> methodList = settings.getMethodList();
             int numImes = methodList.size();
             p.println("  Input Methods: mMethodMapUpdateCount=" + mMethodMapUpdateCount);
             for (int i = 0; i < numImes; i++) {
@@ -5860,6 +5866,7 @@
                 p.println("    curSession=" + c.mCurSession);
             };
             mClientController.forAllClients(clientControllerDump);
+            p.println("  mCurrentUserId=" + mCurrentUserId);
             p.println("  mCurMethodId=" + getSelectedMethodIdLocked());
             client = mCurClient;
             p.println("  mCurClient=" + client + " mCurSeq=" + getSequenceNumberLocked());
@@ -5885,8 +5892,6 @@
                     ? Arrays.toString(mStylusIds.toArray()) : ""));
             p.println("  mSwitchingController:");
             mSwitchingController.dump(p);
-            p.println("  mSettings:");
-            mSettings.dump(p, "    ");
 
             p.println("  mStartInputHistory:");
             mStartInputHistory.dump(pw, "    ");
@@ -6141,7 +6146,7 @@
         }
         synchronized (ImfLock.class) {
             final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved,
-                    mSettings.getUserId(), shellCommand.getErrPrintWriter());
+                    mCurrentUserId, shellCommand.getErrPrintWriter());
             try (PrintWriter pr = shellCommand.getOutPrintWriter()) {
                 for (int userId : userIds) {
                     final List<InputMethodInfo> methods = all
@@ -6186,7 +6191,7 @@
              PrintWriter error = shellCommand.getErrPrintWriter()) {
             synchronized (ImfLock.class) {
                 final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved,
-                        mSettings.getUserId(), shellCommand.getErrPrintWriter());
+                        mCurrentUserId, shellCommand.getErrPrintWriter());
                 for (int userId : userIds) {
                     if (!userHasDebugPriv(userId, shellCommand)) {
                         continue;
@@ -6245,14 +6250,14 @@
             PrintWriter error) {
         boolean failedToEnableUnknownIme = false;
         boolean previouslyEnabled = false;
-        if (userId == mSettings.getUserId()) {
-            if (enabled && !mSettings.getMethodMap().containsKey(imeId)) {
+        final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
+        if (userId == mCurrentUserId) {
+            if (enabled && !settings.getMethodMap().containsKey(imeId)) {
                 failedToEnableUnknownIme = true;
             } else {
                 previouslyEnabled = setInputMethodEnabledLocked(imeId, enabled);
             }
         } else {
-            final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
             if (enabled) {
                 if (!settings.getMethodMap().containsKey(imeId)) {
                     failedToEnableUnknownIme = true;
@@ -6307,7 +6312,7 @@
              PrintWriter error = shellCommand.getErrPrintWriter()) {
             synchronized (ImfLock.class) {
                 final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved,
-                        mSettings.getUserId(), shellCommand.getErrPrintWriter());
+                        mCurrentUserId, shellCommand.getErrPrintWriter());
                 for (int userId : userIds) {
                     if (!userHasDebugPriv(userId, shellCommand)) {
                         continue;
@@ -6347,7 +6352,7 @@
         synchronized (ImfLock.class) {
             try (PrintWriter out = shellCommand.getOutPrintWriter()) {
                 final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved,
-                        mSettings.getUserId(), shellCommand.getErrPrintWriter());
+                        mCurrentUserId, shellCommand.getErrPrintWriter());
                 for (int userId : userIds) {
                     if (!userHasDebugPriv(userId, shellCommand)) {
                         continue;
@@ -6359,15 +6364,16 @@
                     }
                     final String nextIme;
                     final List<InputMethodInfo> nextEnabledImes;
-                    if (userId == mSettings.getUserId()) {
+                    final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
+                    if (userId == mCurrentUserId) {
                         hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */,
                                 SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND);
                         mBindingController.unbindCurrentMethod();
 
                         // Enable default IMEs, disable others
-                        var toDisable = mSettings.getEnabledInputMethodList();
+                        var toDisable = settings.getEnabledInputMethodList();
                         var defaultEnabled = InputMethodInfoUtils.getDefaultEnabledImes(
-                                mContext, mSettings.getMethodList());
+                                mContext, settings.getMethodList());
                         toDisable.removeAll(defaultEnabled);
                         for (InputMethodInfo info : toDisable) {
                             setInputMethodEnabledLocked(info.getId(), false);
@@ -6381,14 +6387,11 @@
                         }
                         updateInputMethodsFromSettingsLocked(true /* enabledMayChange */);
                         InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(
-                                getPackageManagerForUser(mContext, mSettings.getUserId()),
-                                mSettings.getEnabledInputMethodList());
-                        nextIme = mSettings.getSelectedInputMethod();
-                        nextEnabledImes = mSettings.getEnabledInputMethodList();
+                                getPackageManagerForUser(mContext, settings.getUserId()),
+                                settings.getEnabledInputMethodList());
+                        nextIme = settings.getSelectedInputMethod();
+                        nextEnabledImes = settings.getEnabledInputMethodList();
                     } else {
-                        final InputMethodSettings settings =
-                                InputMethodSettingsRepository.get(userId);
-
                         nextEnabledImes = InputMethodInfoUtils.getDefaultEnabledImes(mContext,
                                 settings.getMethodList());
                         nextIme = InputMethodInfoUtils.getMostApplicableDefaultIME(
diff --git a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
index bd73cb6..1938642 100644
--- a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
@@ -23,9 +23,15 @@
 
 import android.app.Notification;
 import android.app.NotificationChannel;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.LoggingOnly;
 import android.content.Context;
 import android.media.AudioAttributes;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.util.Slog;
+import com.android.internal.compat.IPlatformCompat;
 
 /**
  * Stores the latest notification channel information for this notification
@@ -34,14 +40,26 @@
     private static final String TAG = "ChannelExtractor";
     private static final boolean DBG = false;
 
+    /**
+     * Corrects audio attributes for notifications based on characteristics of the notifications.
+     */
+    @ChangeId
+    @LoggingOnly
+    static final long RESTRICT_AUDIO_ATTRIBUTES = 331793339L;
+
     private RankingConfig mConfig;
     private Context mContext;
+    private IPlatformCompat mPlatformCompat;
 
     public void initialize(Context ctx, NotificationUsageStats usageStats) {
         mContext = ctx;
         if (DBG) Slog.d(TAG, "Initializing  " + getClass().getSimpleName() + ".");
     }
 
+    public void setCompatChangeLogger(IPlatformCompat platformCompat) {
+        mPlatformCompat = platformCompat;
+    }
+
     public RankingReconsideration process(NotificationRecord record) {
         if (record == null || record.getNotification() == null) {
             if (DBG) Slog.d(TAG, "skipping empty notification");
@@ -80,6 +98,7 @@
             }
 
             if (updateAttributes) {
+                reportAudioAttributesChanged(record.getUid());
                 NotificationChannel clone = record.getChannel().copy();
                 clone.setSound(clone.getSound(), new AudioAttributes.Builder(attributes)
                         .setUsage(USAGE_NOTIFICATION)
@@ -91,6 +110,17 @@
         return null;
     }
 
+    private void reportAudioAttributesChanged(int uid) {
+        final long id = Binder.clearCallingIdentity();
+        try {
+            mPlatformCompat.reportChangeByUid(RESTRICT_AUDIO_ATTRIBUTES, uid);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Unexpected exception while reporting to changecompat", e);
+        } finally {
+            Binder.restoreCallingIdentity(id);
+        }
+    }
+
     @Override
     public void setConfig(RankingConfig config) {
         mConfig = config;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 8075ae0..b48cad2 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -220,6 +220,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.LauncherApps;
+import android.content.pm.ModuleInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PackageManagerInternal;
@@ -675,6 +676,10 @@
 
     private static final int DB_VERSION = 1;
 
+
+    private static final String ADSERVICES_MODULE_PKG_NAME =
+            "com.android.adservices";
+
     private static final String TAG_NOTIFICATION_POLICY = "notification-policy";
     private static final String ATTR_VERSION = "version";
 
@@ -736,6 +741,8 @@
 
     private AppOpsManager.OnOpChangedListener mAppOpsListener;
 
+    private ModuleInfo mAdservicesModuleInfo;
+
     static class Archive {
         final SparseArray<Boolean> mEnabled;
         final int mBufferSize;
@@ -2508,12 +2515,8 @@
                 mAppOps,
                 mUserProfiles,
                 mShowReviewPermissionsNotification);
-        mRankingHelper = new RankingHelper(getContext(),
-                mRankingHandler,
-                mPreferencesHelper,
-                mZenModeHelper,
-                mUsageStats,
-                extractorNames);
+        mRankingHelper = new RankingHelper(getContext(), mRankingHandler, mPreferencesHelper,
+                mZenModeHelper, mUsageStats, extractorNames, mPlatformCompat);
         mSnoozeHelper = snoozeHelper;
         mGroupHelper = groupHelper;
         mHistoryManager = historyManager;
@@ -2938,6 +2941,15 @@
                 mZenModeHelper.setDeviceEffectsApplier(
                         new DefaultDeviceEffectsApplier(getContext()));
             }
+            List<ModuleInfo> moduleInfoList =
+            mPackageManagerClient.getInstalledModules(
+                PackageManager.MATCH_DEBUG_TRIAGED_MISSING);
+            // Cache adservices module info
+            for (ModuleInfo mi : moduleInfoList) {
+                if (Objects.equals(mi.getApexModuleName(), ADSERVICES_MODULE_PKG_NAME)) {
+                    mAdservicesModuleInfo = mi;
+                }
+            }
         } else if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
             mSnoozeHelper.scheduleRepostsForPersistedNotifications(System.currentTimeMillis());
         } else if (phase == SystemService.PHASE_DEVICE_SPECIFIC_SERVICES_READY) {
@@ -7687,13 +7699,27 @@
     private boolean canBeNonDismissible(ApplicationInfo ai, Notification notification) {
         return notification.isMediaNotification() || isEnterpriseExempted(ai)
                 || notification.isStyle(Notification.CallStyle.class)
-                || isDefaultSearchSelectorPackage(ai.packageName);
+                || isDefaultSearchSelectorPackage(ai.packageName)
+                || isDefaultAdservicesPackage(ai.packageName);
     }
 
     private boolean isDefaultSearchSelectorPackage(String pkg) {
         return Objects.equals(mDefaultSearchSelectorPkg, pkg);
     }
 
+    private boolean isDefaultAdservicesPackage(String pkg) {
+        if (mAdservicesModuleInfo == null) {
+            return false;
+        }
+        // Handles the special package structure for mainline modules
+        for (String apkName : mAdservicesModuleInfo.getApkInApexPackageNames()) {
+            if (Objects.equals(apkName, pkg)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private boolean isEnterpriseExempted(ApplicationInfo ai) {
         // Check if the app is an organization admin app
         // TODO(b/234609037): Replace with new DPM APIs to check if organization admin
diff --git a/services/core/java/com/android/server/notification/NotificationSignalExtractor.java b/services/core/java/com/android/server/notification/NotificationSignalExtractor.java
index 24c1d59..f0358d1 100644
--- a/services/core/java/com/android/server/notification/NotificationSignalExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationSignalExtractor.java
@@ -17,6 +17,7 @@
 package com.android.server.notification;
 
 import android.content.Context;
+import com.android.internal.compat.IPlatformCompat;
 
 /**
  * Extracts signals that will be useful to the {@link NotificationComparator} and caches them
@@ -52,4 +53,6 @@
      *               DND.
      */
     void setZenHelper(ZenModeHelper helper);
+
+    default void setCompatChangeLogger(IPlatformCompat platformCompat){};
 }
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 50ca984..461bd9c 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -1387,8 +1387,7 @@
     public void updateFixedImportance(List<UserInfo> users) {
         for (UserInfo user : users) {
             List<PackageInfo> packages = mPm.getInstalledPackagesAsUser(
-                    PackageManager.PackageInfoFlags.of(PackageManager.MATCH_SYSTEM_ONLY),
-                    user.getUserHandle().getIdentifier());
+                    0, user.getUserHandle().getIdentifier());
             for (PackageInfo pi : packages) {
                 boolean fixed = mPermissionHelper.isPermissionFixed(
                         pi.packageName, user.getUserHandle().getIdentifier());
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 68e0eaa..7756801 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -15,6 +15,9 @@
  */
 package com.android.server.notification;
 
+import static android.app.Flags.restrictAudioAttributesAlarm;
+import static android.app.Flags.restrictAudioAttributesCall;
+import static android.app.Flags.restrictAudioAttributesMedia;
 import static android.app.Flags.sortSectionByTime;
 import static android.app.NotificationManager.IMPORTANCE_MIN;
 import static android.text.TextUtils.formatSimple;
@@ -27,6 +30,7 @@
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.compat.IPlatformCompat;
 import com.android.tools.r8.keepanno.annotations.KeepItemKind;
 import com.android.tools.r8.keepanno.annotations.KeepTarget;
 import com.android.tools.r8.keepanno.annotations.UsesReflection;
@@ -56,7 +60,8 @@
                         methodName = "<init>")
             })
     public RankingHelper(Context context, RankingHandler rankingHandler, RankingConfig config,
-            ZenModeHelper zenHelper, NotificationUsageStats usageStats, String[] extractorNames) {
+            ZenModeHelper zenHelper, NotificationUsageStats usageStats, String[] extractorNames,
+            IPlatformCompat platformCompat) {
         mContext = context;
         mRankingHandler = rankingHandler;
         if (sortSectionByTime()) {
@@ -75,6 +80,10 @@
                 extractor.initialize(mContext, usageStats);
                 extractor.setConfig(config);
                 extractor.setZenHelper(zenHelper);
+                if (restrictAudioAttributesAlarm() || restrictAudioAttributesMedia()
+                        || restrictAudioAttributesCall()) {
+                    extractor.setCompatChangeLogger(platformCompat);
+                }
                 mSignalExtractors[i] = extractor;
             } catch (ClassNotFoundException e) {
                 Slog.w(TAG, "Couldn't find extractor " + extractorNames[i] + ".", e);
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 4c653f6..fe9c3f2 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -50,6 +50,7 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.IShortcutService;
 import android.content.pm.LauncherApps;
+import android.content.pm.LauncherApps.ShortcutChangeCallback;
 import android.content.pm.LauncherApps.ShortcutQuery;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
@@ -151,6 +152,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
+import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.Consumer;
@@ -320,12 +322,11 @@
 
     private final Handler mHandler;
 
-    @GuardedBy("mLock")
-    private final ArrayList<ShortcutChangeListener> mListeners = new ArrayList<>(1);
+    private final CopyOnWriteArrayList<ShortcutChangeListener> mListeners =
+            new CopyOnWriteArrayList<>();
 
-    @GuardedBy("mLock")
-    private final ArrayList<LauncherApps.ShortcutChangeCallback> mShortcutChangeCallbacks =
-            new ArrayList<>(1);
+    private final CopyOnWriteArrayList<ShortcutChangeCallback> mShortcutChangeCallbacks =
+            new CopyOnWriteArrayList<>();
 
     private final AtomicLong mRawLastResetTime = new AtomicLong(0);
 
@@ -1841,18 +1842,11 @@
             @UserIdInt final int userId) {
         return () -> {
             try {
-                final ArrayList<ShortcutChangeListener> copy;
-                synchronized (mLock) {
-                    if (!isUserUnlockedL(userId)) {
-                        return;
-                    }
-
-                    copy = new ArrayList<>(mListeners);
+                if (!isUserUnlockedL(userId)) {
+                    return;
                 }
                 // Note onShortcutChanged() needs to be called with the system service permissions.
-                for (int i = copy.size() - 1; i >= 0; i--) {
-                    copy.get(i).onShortcutChanged(packageName, userId);
-                }
+                mListeners.forEach(listener -> listener.onShortcutChanged(packageName, userId));
             } catch (Exception ignore) {
             }
         };
@@ -1867,22 +1861,17 @@
         final UserHandle user = UserHandle.of(userId);
         injectPostToHandler(() -> {
             try {
-                final ArrayList<LauncherApps.ShortcutChangeCallback> copy;
-                synchronized (mLock) {
-                    if (!isUserUnlockedL(userId)) {
-                        return;
-                    }
-
-                    copy = new ArrayList<>(mShortcutChangeCallbacks);
+                if (!isUserUnlockedL(userId)) {
+                    return;
                 }
-                for (int i = copy.size() - 1; i >= 0; i--) {
+                mShortcutChangeCallbacks.forEach(callback -> {
                     if (!CollectionUtils.isEmpty(changedList)) {
-                        copy.get(i).onShortcutsAddedOrUpdated(packageName, changedList, user);
+                        callback.onShortcutsAddedOrUpdated(packageName, changedList, user);
                     }
                     if (!CollectionUtils.isEmpty(removedList)) {
-                        copy.get(i).onShortcutsRemoved(packageName, removedList, user);
+                        callback.onShortcutsRemoved(packageName, removedList, user);
                     }
-                }
+                });
             } catch (Exception ignore) {
             }
         });
@@ -3425,17 +3414,13 @@
 
         @Override
         public void addListener(@NonNull ShortcutChangeListener listener) {
-            synchronized (mLock) {
-                mListeners.add(Objects.requireNonNull(listener));
-            }
+            mListeners.add(Objects.requireNonNull(listener));
         }
 
         @Override
         public void addShortcutChangeCallback(
                 @NonNull LauncherApps.ShortcutChangeCallback callback) {
-            synchronized (mLock) {
-                mShortcutChangeCallbacks.add(Objects.requireNonNull(callback));
-            }
+            mShortcutChangeCallbacks.add(Objects.requireNonNull(callback));
         }
 
         @Override
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index d060c7c..54cb9c9 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -4885,7 +4885,6 @@
         if (type == WAKE_TYPE_PARTIAL) {
             // Only care about partial wake locks, since full wake locks
             // will be canceled when the user puts the screen to sleep.
-            aggregateLastWakeupUptimeLocked(elapsedRealtimeMs, uptimeMs);
             if (historyName == null) {
                 historyName = name;
             }
@@ -5205,20 +5204,14 @@
     }
 
     @GuardedBy("this")
-    void aggregateLastWakeupUptimeLocked(long elapsedRealtimeMs, long uptimeMs) {
+    public void noteWakeupReasonLocked(String reason, long elapsedRealtimeMs, long uptimeMs) {
         if (mLastWakeupReason != null) {
             long deltaUptimeMs = uptimeMs - mLastWakeupUptimeMs;
             SamplingTimer timer = getWakeupReasonTimerLocked(mLastWakeupReason);
             timer.add(deltaUptimeMs * 1000, 1, elapsedRealtimeMs); // time in in microseconds
             mFrameworkStatsLogger.kernelWakeupReported(deltaUptimeMs * 1000, mLastWakeupReason,
                     mLastWakeupElapsedTimeMs);
-            mLastWakeupReason = null;
         }
-    }
-
-    @GuardedBy("this")
-    public void noteWakeupReasonLocked(String reason, long elapsedRealtimeMs, long uptimeMs) {
-        aggregateLastWakeupUptimeLocked(elapsedRealtimeMs, uptimeMs);
         mHistory.recordWakeupEvent(elapsedRealtimeMs, uptimeMs, reason);
         mLastWakeupReason = reason;
         mLastWakeupUptimeMs = uptimeMs;
diff --git a/services/core/java/com/android/server/security/FileIntegrityService.java b/services/core/java/com/android/server/security/FileIntegrityService.java
index bb4876b..5b501e1 100644
--- a/services/core/java/com/android/server/security/FileIntegrityService.java
+++ b/services/core/java/com/android/server/security/FileIntegrityService.java
@@ -170,6 +170,10 @@
         @Override
         public int setupFsverity(android.os.IInstalld.IFsveritySetupAuthToken authToken,
                 String filePath, String packageName) throws RemoteException {
+            getContext().enforceCallingPermission(android.Manifest.permission.SETUP_FSVERITY,
+                    "Permission android.permission.SETUP_FSVERITY not grantted to access "
+                    + "FileIntegrityManager#setupFsverity");
+
             Objects.requireNonNull(authToken);
             Objects.requireNonNull(filePath);
             Objects.requireNonNull(packageName);
diff --git a/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java b/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
index c5d3333..3619253 100644
--- a/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
+++ b/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
@@ -64,12 +64,19 @@
 
     private static final int PACKET_LOSS_PERCENT_UNAVAILABLE = -1;
 
+    // Ignore the packet loss detection result if the expected packet number is smaller than 10.
+    // Solarwinds NPM uses 10 ICMP echos to calculate packet loss rate (as per
+    // https://thwack.solarwinds.com/products/network-performance-monitor-npm/f/forum/63829/how-is-packet-loss-calculated)
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    static final int MIN_VALID_EXPECTED_RX_PACKET_NUM = 10;
+
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(
             prefix = {"PACKET_LOSS_"},
             value = {
                 PACKET_LOSS_RATE_VALID,
                 PACKET_LOSS_RATE_INVALID,
+                PACKET_LOSS_UNUSUAL_SEQ_NUM_LEAP,
             })
     @Target({ElementType.TYPE_USE})
     private @interface PacketLossResultType {}
@@ -84,11 +91,23 @@
      * <ul>
      *   <li>The replay window did not proceed and thus all packets might have been delivered out of
      *       order
+     *   <li>The expected received packet number is too small and thus the detection result is not
+     *       reliable
      *   <li>There are unexpected errors
      * </ul>
      */
     private static final int PACKET_LOSS_RATE_INVALID = 1;
 
+    /**
+     * The sequence number increase is unusually large and might be caused an intentional leap on
+     * the server's downlink
+     *
+     * <p>Inbound sequence number will not always increase consecutively. During load balancing the
+     * server might add a big leap on the sequence number intentionally. In such case a high packet
+     * loss rate does not always indicate a lossy network
+     */
+    private static final int PACKET_LOSS_UNUSUAL_SEQ_NUM_LEAP = 2;
+
     // For VoIP, losses between 5% and 10% of the total packet stream will affect the quality
     // significantly (as per "Computer Networking for LANS to WANS: Hardware, Software and
     // Security"). For audio and video streaming, above 10-12% packet loss is unacceptable (as per
@@ -98,8 +117,12 @@
 
     private static final int POLL_IPSEC_STATE_INTERVAL_SECONDS_DEFAULT = 20;
 
+    // By default, there's no maximum limit enforced
+    private static final int MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED = -1;
+
     private long mPollIpSecStateIntervalMs;
-    private final int mPacketLossRatePercentThreshold;
+    private int mPacketLossRatePercentThreshold;
+    private int mMaxSeqNumIncreasePerSecond;
 
     @NonNull private final Handler mHandler;
     @NonNull private final PowerManager mPowerManager;
@@ -138,6 +161,7 @@
 
         mPollIpSecStateIntervalMs = getPollIpSecStateIntervalMs(carrierConfig);
         mPacketLossRatePercentThreshold = getPacketLossRatePercentThreshold(carrierConfig);
+        mMaxSeqNumIncreasePerSecond = getMaxSeqNumIncreasePerSecond(carrierConfig);
 
         // Register for system broadcasts to monitor idle mode change
         final IntentFilter intentFilter = new IntentFilter();
@@ -202,6 +226,24 @@
         return IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_DEFAULT;
     }
 
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    static int getMaxSeqNumIncreasePerSecond(@Nullable PersistableBundleWrapper carrierConfig) {
+        int maxSeqNumIncrease = MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED;
+        if (Flags.handleSeqNumLeap() && carrierConfig != null) {
+            maxSeqNumIncrease =
+                    carrierConfig.getInt(
+                            VcnManager.VCN_NETWORK_SELECTION_MAX_SEQ_NUM_INCREASE_PER_SECOND_KEY,
+                            MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED);
+        }
+
+        if (maxSeqNumIncrease < MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED) {
+            logE(TAG, "Invalid value of MAX_SEQ_NUM_INCREASE_PER_SECOND_KEY " + maxSeqNumIncrease);
+            return MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED;
+        }
+
+        return maxSeqNumIncrease;
+    }
+
     @Override
     protected void onSelectedUnderlyingNetworkChanged() {
         if (!isSelectedUnderlyingNetwork()) {
@@ -237,6 +279,11 @@
         // The already scheduled event will not be affected. The followup events will be scheduled
         // with the new interval
         mPollIpSecStateIntervalMs = getPollIpSecStateIntervalMs(carrierConfig);
+
+        if (Flags.handleSeqNumLeap()) {
+            mPacketLossRatePercentThreshold = getPacketLossRatePercentThreshold(carrierConfig);
+            mMaxSeqNumIncreasePerSecond = getMaxSeqNumIncreasePerSecond(carrierConfig);
+        }
     }
 
     @Override
@@ -339,7 +386,10 @@
 
         final PacketLossCalculationResult calculateResult =
                 mPacketLossCalculator.getPacketLossRatePercentage(
-                        mLastIpSecTransformState, state, getLogPrefix());
+                        mLastIpSecTransformState,
+                        state,
+                        mMaxSeqNumIncreasePerSecond,
+                        getLogPrefix());
 
         if (calculateResult.getResultType() == PACKET_LOSS_RATE_INVALID) {
             return;
@@ -356,11 +406,18 @@
         mLastIpSecTransformState = state;
         if (calculateResult.getPacketLossRatePercent() < mPacketLossRatePercentThreshold) {
             logV(logMsg);
+
+            // In both "valid" or "unusual_seq_num_leap" cases, notify that the network has passed
+            // the validation
             onValidationResultReceivedInternal(false /* isFailed */);
         } else {
             logInfo(logMsg);
-            onValidationResultReceivedInternal(true /* isFailed */);
 
+            if (calculateResult.getResultType() == PACKET_LOSS_RATE_VALID) {
+                onValidationResultReceivedInternal(true /* isFailed */);
+            }
+
+            // In both "valid" or "unusual_seq_num_leap" cases, trigger network validation
             if (Flags.validateNetworkOnIpsecLoss()) {
                 // Trigger re-validation of the underlying network; if it fails, the VCN will
                 // attempt to migrate away.
@@ -376,6 +433,7 @@
         public PacketLossCalculationResult getPacketLossRatePercentage(
                 @NonNull IpSecTransformState oldState,
                 @NonNull IpSecTransformState newState,
+                int maxSeqNumIncreasePerSecond,
                 String logPrefix) {
             logVIpSecTransform("oldState", oldState, logPrefix);
             logVIpSecTransform("newState", newState, logPrefix);
@@ -392,6 +450,22 @@
                 return PacketLossCalculationResult.invalid();
             }
 
+            boolean isUnusualSeqNumLeap = false;
+
+            // Handle sequence number leap
+            if (Flags.handleSeqNumLeap()
+                    && maxSeqNumIncreasePerSecond != MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED) {
+                final long timeDiffMillis =
+                        newState.getTimestampMillis() - oldState.getTimestampMillis();
+                final long maxSeqNumIncrease = timeDiffMillis * maxSeqNumIncreasePerSecond / 1000;
+
+                // Sequence numbers are unsigned 32-bit values. If maxSeqNumIncrease overflows,
+                // isUnusualSeqNumLeap can never be true.
+                if (maxSeqNumIncrease >= 0 && newSeqHi - oldSeqHi >= maxSeqNumIncrease) {
+                    isUnusualSeqNumLeap = true;
+                }
+            }
+
             // Get the expected packet count by assuming there is no packet loss. In this case, SA
             // should receive all packets whose sequence numbers are smaller than the lower bound of
             // the replay window AND the packets received within the window.
@@ -411,6 +485,11 @@
                             + " actualPktCntDiff: "
                             + actualPktCntDiff);
 
+            if (Flags.handleSeqNumLeap() && expectedPktCntDiff < MIN_VALID_EXPECTED_RX_PACKET_NUM) {
+                // The sample size is too small to ensure a reliable detection result
+                return PacketLossCalculationResult.invalid();
+            }
+
             if (expectedPktCntDiff < 0
                     || expectedPktCntDiff == 0
                     || actualPktCntDiff < 0
@@ -420,7 +499,9 @@
             }
 
             final int percent = 100 - (int) (actualPktCntDiff * 100 / expectedPktCntDiff);
-            return PacketLossCalculationResult.valid(percent);
+            return isUnusualSeqNumLeap
+                    ? PacketLossCalculationResult.unusualSeqNumLeap(percent)
+                    : PacketLossCalculationResult.valid(percent);
         }
     }
 
@@ -462,6 +543,11 @@
                     PACKET_LOSS_RATE_INVALID, PACKET_LOSS_PERCENT_UNAVAILABLE);
         }
 
+        /** Construct an instance indicating that there is an unusual sequence number leap */
+        public static PacketLossCalculationResult unusualSeqNumLeap(int percent) {
+            return new PacketLossCalculationResult(PACKET_LOSS_UNUSUAL_SEQ_NUM_LEAP, percent);
+        }
+
         @PacketLossResultType
         public int getResultType() {
             return mResultType;
diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java b/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java
index a1b212f..b9b1060 100644
--- a/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java
+++ b/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java
@@ -272,6 +272,11 @@
         }
     }
 
+    protected static void logE(String className, String msgWithPrefix) {
+        Slog.w(className, msgWithPrefix);
+        LOCAL_LOG.log("[ERROR ] " + className + msgWithPrefix);
+    }
+
     protected static void logWtf(String className, String msgWithPrefix) {
         Slog.wtf(className, msgWithPrefix);
         LOCAL_LOG.log("[WTF ] " + className + msgWithPrefix);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 87c5b7b..00d42e0 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2003,7 +2003,12 @@
         }
         // Update directly because the app which will change the orientation of display is ready.
         if (mDisplayRotation.updateOrientation(getOrientation(), false /* forceUpdate */)) {
-            sendNewConfiguration();
+            // Run rotation change on display thread. See Transition#shouldApplyOnDisplayThread().
+            mWmService.mH.post(() -> {
+                synchronized (mWmService.mGlobalLock) {
+                    sendNewConfiguration();
+                }
+            });
             return;
         }
         if (mRemoteDisplayChangeController.isWaitingForRemoteDisplayChange()) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ed88b5a..143605a 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2446,6 +2446,9 @@
             ProtoLog.i(WM_DEBUG_SCREEN_ON,
                     "Relayout %s: oldVis=%d newVis=%d. %s", win, oldVisibility,
                             viewVisibility, new RuntimeException().fillInStackTrace());
+            if (becameVisible) {
+                onWindowVisible(win);
+            }
 
             win.setDisplayLayoutNeeded();
             win.mGivenInsetsPending = (flags & WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0;
@@ -10168,7 +10171,7 @@
      * Called to notify WMS that the specified window has become visible. This shows a Toast if the
      * window is deemed to hold sensitive content.
      */
-    void onWindowVisible(@NonNull WindowState w) {
+    private void onWindowVisible(@NonNull WindowState w) {
         showToastIfBlockingScreenCapture(w);
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index d3baedc..2fcee50 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -28,7 +28,6 @@
 import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
 import static android.os.PowerManager.DRAW_WAKE_LOCK;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
-import static android.permission.flags.Flags.sensitiveContentImprovements;
 import static android.view.SurfaceControl.Transaction;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
@@ -2139,9 +2138,6 @@
                 }
             }
             setDisplayLayoutNeeded();
-            if (sensitiveContentImprovements() && visible) {
-                mWmService.onWindowVisible(this);
-            }
         }
     }
 
diff --git a/services/core/jni/linux/usb/f_accessory.h b/services/core/jni/linux/usb/f_accessory.h
new file mode 100644
index 0000000..abd864c
--- /dev/null
+++ b/services/core/jni/linux/usb/f_accessory.h
@@ -0,0 +1,34 @@
+/*
+ * This file is auto-generated. Modifications will be lost.
+ *
+ * See https://android.googlesource.com/platform/bionic/+/master/libc/kernel/
+ * for more information.
+ */
+#ifndef _UAPI_LINUX_USB_F_ACCESSORY_H
+#define _UAPI_LINUX_USB_F_ACCESSORY_H
+#define USB_ACCESSORY_VENDOR_ID 0x18D1
+#define USB_ACCESSORY_PRODUCT_ID 0x2D00
+#define USB_ACCESSORY_ADB_PRODUCT_ID 0x2D01
+#define ACCESSORY_STRING_MANUFACTURER 0
+#define ACCESSORY_STRING_MODEL 1
+#define ACCESSORY_STRING_DESCRIPTION 2
+#define ACCESSORY_STRING_VERSION 3
+#define ACCESSORY_STRING_URI 4
+#define ACCESSORY_STRING_SERIAL 5
+#define ACCESSORY_GET_PROTOCOL 51
+#define ACCESSORY_SEND_STRING 52
+#define ACCESSORY_START 53
+#define ACCESSORY_REGISTER_HID 54
+#define ACCESSORY_UNREGISTER_HID 55
+#define ACCESSORY_SET_HID_REPORT_DESC 56
+#define ACCESSORY_SEND_HID_EVENT 57
+#define ACCESSORY_SET_AUDIO_MODE 58
+#define ACCESSORY_GET_STRING_MANUFACTURER _IOW('M', 1, char[256])
+#define ACCESSORY_GET_STRING_MODEL _IOW('M', 2, char[256])
+#define ACCESSORY_GET_STRING_DESCRIPTION _IOW('M', 3, char[256])
+#define ACCESSORY_GET_STRING_VERSION _IOW('M', 4, char[256])
+#define ACCESSORY_GET_STRING_URI _IOW('M', 5, char[256])
+#define ACCESSORY_GET_STRING_SERIAL _IOW('M', 6, char[256])
+#define ACCESSORY_IS_START_REQUESTED _IO('M', 7)
+#define ACCESSORY_GET_AUDIO_MODE _IO('M', 8)
+#endif
diff --git a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
index dedb687..b1673e2 100644
--- a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
@@ -26,6 +26,7 @@
 import android.credentials.CredentialProviderInfo;
 import android.credentials.GetCandidateCredentialsException;
 import android.credentials.GetCandidateCredentialsResponse;
+import android.credentials.GetCredentialException;
 import android.credentials.GetCredentialRequest;
 import android.credentials.GetCredentialResponse;
 import android.credentials.IGetCandidateCredentialsCallback;
@@ -159,24 +160,26 @@
     public void onFinalErrorReceived(ComponentName componentName, String errorType,
             String message) {
         Slog.d(TAG, "onFinalErrorReceived");
+        if (GetCredentialException.TYPE_USER_CANCELED.equals(errorType)) {
+            Slog.d(TAG, "User canceled but session is not being terminated");
+            return;
+        }
         respondToFinalReceiverWithFailureAndFinish(errorType, message);
     }
 
     @Override
     public void onUiCancellation(boolean isUserCancellation) {
-        String exception = GetCandidateCredentialsException.TYPE_USER_CANCELED;
-        String message = "User cancelled the selector";
-        if (!isUserCancellation) {
-            exception = GetCandidateCredentialsException.TYPE_INTERRUPTED;
-            message = "The UI was interrupted - please try again.";
-        }
-        mRequestSessionMetric.collectFrameworkException(exception);
-        respondToFinalReceiverWithFailureAndFinish(exception, message);
+        Slog.d(TAG, "User canceled but session is not being terminated");
     }
 
     private void respondToFinalReceiverWithFailureAndFinish(
             String exception, String message
     ) {
+        if (mRequestSessionStatus == RequestSessionStatus.COMPLETE) {
+            Slog.w(TAG, "Request has already been completed. This is strange.");
+            return;
+        }
+
         if (mAutofillCallback != null) {
             Bundle resultData = new Bundle();
             resultData.putStringArray(
@@ -221,6 +224,19 @@
     public void onFinalResponseReceived(ComponentName componentName,
             GetCredentialResponse response) {
         Slog.d(TAG, "onFinalResponseReceived");
+        if (mRequestSessionStatus == RequestSessionStatus.COMPLETE) {
+            Slog.w(TAG, "Request has already been completed. This is strange.");
+            return;
+        }
+        respondToFinalReceiverWithResponseAndFinish(response);
+    }
+
+    private void respondToFinalReceiverWithResponseAndFinish(GetCredentialResponse response) {
+        if (mRequestSessionStatus == RequestSessionStatus.COMPLETE) {
+            Slog.w(TAG, "Request has already been completed. This is strange.");
+            return;
+        }
+
         if (this.mAutofillCallback != null) {
             Slog.d(TAG, "onFinalResponseReceived sending through final receiver");
             Bundle resultData = new Bundle();
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
index 5897d76..0877146 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -920,6 +920,7 @@
     @Test
     public void testEvenDimmer() throws IOException {
         when(mFlags.isEvenDimmerEnabled()).thenReturn(true);
+        when(mResources.getBoolean(R.bool.config_evenDimmerEnabled)).thenReturn(true);
         setupDisplayDeviceConfigFromDisplayConfigFile(getContent(getValidLuxThrottling(),
                 getValidProxSensor(), /* includeIdleMode= */ false, /* enableEvenDimmer */ true));
 
diff --git a/services/tests/displayservicetests/src/com/android/server/display/ExternalDisplayPolicyTest.java b/services/tests/displayservicetests/src/com/android/server/display/ExternalDisplayPolicyTest.java
index ea08be4..fe7bbe0 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/ExternalDisplayPolicyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/ExternalDisplayPolicyTest.java
@@ -157,6 +157,7 @@
         verify(mMockedLogicalDisplayMapper, never()).setDisplayEnabledLocked(any(), anyBoolean());
         verify(mMockedDisplayNotificationManager, times(2))
                 .onHighTemperatureExternalDisplayNotAllowed();
+        verify(mMockedInjector, never()).onExternalDisplayReadyToBeEnabled(anyInt());
     }
 
     @Test
@@ -167,6 +168,7 @@
         verify(mMockedLogicalDisplayMapper, never()).setDisplayEnabledLocked(any(), anyBoolean());
         verify(mMockedDisplayNotificationManager, never())
                 .onHighTemperatureExternalDisplayNotAllowed();
+        verify(mMockedInjector, never()).onExternalDisplayReadyToBeEnabled(anyInt());
     }
 
     @Test
@@ -184,6 +186,7 @@
         // Expected only 1 invocation, upon critical temperature.
         verify(mMockedDisplayNotificationManager).onHighTemperatureExternalDisplayNotAllowed();
         verify(mMockedExternalDisplayStatsService).onDisplayDisabled(eq(EXTERNAL_DISPLAY_ID));
+        verify(mMockedInjector, never()).onExternalDisplayReadyToBeEnabled(anyInt());
     }
 
     @Test
@@ -191,6 +194,7 @@
         mExternalDisplayPolicy.setExternalDisplayEnabledLocked(mMockedLogicalDisplay,
                 /*enabled=*/ true);
         assertDisplaySetEnabled(/*enabled=*/ true);
+        verify(mMockedInjector).onExternalDisplayReadyToBeEnabled(eq(EXTERNAL_DISPLAY_ID));
     }
 
     @Test
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
index 0efd046..d670b13 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
@@ -1945,7 +1945,7 @@
         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
         votesByDisplay.put(DISPLAY_ID_2, votes);
 
-        director.getDisplayObserver().onDisplayAdded(DISPLAY_ID_2);
+        director.getDisplayObserver().onExternalDisplayReadyToBeEnabled(DISPLAY_ID_2);
         director.injectVotesByDisplay(votesByDisplay);
 
         var desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID_2);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java
index d0dd921..1c192ef 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java
@@ -38,7 +38,6 @@
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.res.Resources;
-import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManagerInternal;
 import android.os.Handler;
 import android.os.Looper;
@@ -109,7 +108,7 @@
     private Context mContext;
     private DisplayModeDirector.Injector mInjector;
     private Handler mHandler;
-    private DisplayManager.DisplayListener mObserver;
+    private DisplayModeDirector.DisplayObserver mObserver;
     private Resources mResources;
     @Mock
     private DisplayManagerFlags mDisplayManagerFlags;
@@ -161,6 +160,7 @@
                 .isEqualTo(null);
 
         // Testing that the vote is not added when display is added because feature is disabled
+        mObserver.onExternalDisplayReadyToBeEnabled(EXTERNAL_DISPLAY);
         mObserver.onDisplayAdded(EXTERNAL_DISPLAY);
         assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
                 .isEqualTo(null);
@@ -194,6 +194,7 @@
         init();
         assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
                 .isEqualTo(null);
+        mObserver.onExternalDisplayReadyToBeEnabled(EXTERNAL_DISPLAY);
         mObserver.onDisplayAdded(EXTERNAL_DISPLAY);
         assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
                 .isEqualTo(null);
@@ -245,6 +246,7 @@
         init();
         assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
                 .isEqualTo(null);
+        mObserver.onExternalDisplayReadyToBeEnabled(EXTERNAL_DISPLAY);
         mObserver.onDisplayAdded(EXTERNAL_DISPLAY);
         assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
                 .isEqualTo(null);
@@ -277,6 +279,7 @@
         init();
         assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
                 .isEqualTo(null);
+        mObserver.onExternalDisplayReadyToBeEnabled(DEFAULT_DISPLAY);
         mObserver.onDisplayAdded(DEFAULT_DISPLAY);
         assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE))
                 .isEqualTo(expectedResolutionVote);
@@ -298,6 +301,7 @@
                 .thenReturn(MAX_HEIGHT);
         init();
         assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null);
+        mObserver.onExternalDisplayReadyToBeEnabled(DEFAULT_DISPLAY);
         mObserver.onDisplayAdded(DEFAULT_DISPLAY);
         assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null);
         assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null);
@@ -317,6 +321,7 @@
                 .thenReturn(MAX_HEIGHT);
         init();
         assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null);
+        mObserver.onExternalDisplayReadyToBeEnabled(EXTERNAL_DISPLAY);
         mObserver.onDisplayAdded(EXTERNAL_DISPLAY);
         assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null);
         assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(
@@ -336,6 +341,7 @@
         when(mDisplayManagerFlags.isExternalDisplayLimitModeEnabled()).thenReturn(true);
         init();
         assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null);
+        mObserver.onExternalDisplayReadyToBeEnabled(EXTERNAL_DISPLAY);
         mObserver.onDisplayAdded(EXTERNAL_DISPLAY);
         assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null);
         assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null);
@@ -358,6 +364,7 @@
                 .thenReturn(true);
         init();
         assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null);
+        mObserver.onExternalDisplayReadyToBeEnabled(EXTERNAL_DISPLAY);
         mObserver.onDisplayAdded(EXTERNAL_DISPLAY);
         assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(
                 Vote.forPhysicalRefreshRates(
@@ -381,6 +388,7 @@
                 .thenReturn(true);
         init();
         assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null);
+        mObserver.onExternalDisplayReadyToBeEnabled(EXTERNAL_DISPLAY);
         mObserver.onDisplayAdded(EXTERNAL_DISPLAY);
         assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null);
     }
@@ -395,6 +403,7 @@
         when(mDisplayManagerFlags.isDisplaysRefreshRatesSynchronizationEnabled()).thenReturn(true);
         init();
         assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null);
+        mObserver.onExternalDisplayReadyToBeEnabled(EXTERNAL_DISPLAY);
         mObserver.onDisplayAdded(EXTERNAL_DISPLAY);
         assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null);
     }
diff --git a/services/tests/servicestests/src/com/android/server/pm/TEST_MAPPING b/services/tests/servicestests/src/com/android/server/pm/TEST_MAPPING
index 861562d..305108e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/TEST_MAPPING
+++ b/services/tests/servicestests/src/com/android/server/pm/TEST_MAPPING
@@ -1,31 +1,11 @@
 {
   "presubmit": [
     {
-      "name": "FrameworksServicesTests",
-      "options": [
-        {
-          "include-filter": "com.android.server.pm."
-        },
-        {
-          "include-annotation": "android.platform.test.annotations.Presubmit"
-        },
-        {
-          "exclude-annotation": "androidx.test.filters.FlakyTest"
-        },
-        {
-          "exclude-annotation": "org.junit.Ignore"
-        }
-      ]
+      "name": "FrameworksServicesTests_pm_presubmit"
     }
   ],
   "postsubmit": [
     {
-      // Presubmit is intentional here while testing with SLO checker.
-      // Tests are flaky, waiting to bypass.
-      "name": "FrameworksServicesTests_pm_presubmit"
-    },
-    {
-      // Leave postsubmit here when migrating
       "name": "FrameworksServicesTests_pm_postsubmit"
     }
   ]
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
index ad25d76..770712a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
@@ -26,14 +26,18 @@
 import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
 import static android.media.AudioAttributes.USAGE_UNKNOWN;
 import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
+import static com.android.server.notification.NotificationChannelExtractor.RESTRICT_AUDIO_ATTRIBUTES;
 import static com.google.common.truth.Truth.assertThat;
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNull;
 
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.Flags;
@@ -43,12 +47,14 @@
 import android.app.Person;
 import android.media.AudioAttributes;
 import android.net.Uri;
+import android.os.RemoteException;
 import android.os.UserHandle;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.Settings;
 import android.service.notification.StatusBarNotification;
 
+import com.android.internal.compat.IPlatformCompat;
 import com.android.server.UiServiceTestCase;
 
 import org.junit.Before;
@@ -60,6 +66,8 @@
 public class NotificationChannelExtractorTest extends UiServiceTestCase {
 
     @Mock RankingConfig mConfig;
+    @Mock
+    IPlatformCompat mPlatformCompat;
 
     @Rule
     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
@@ -73,6 +81,7 @@
         mExtractor = new NotificationChannelExtractor();
         mExtractor.setConfig(mConfig);
         mExtractor.initialize(mContext, null);
+        mExtractor.setCompatChangeLogger(mPlatformCompat);
     }
 
     private NotificationRecord getRecord(NotificationChannel channel, Notification n) {
@@ -82,7 +91,7 @@
     }
 
     @Test
-    public void testExtractsUpdatedConversationChannel() {
+    public void testExtractsUpdatedConversationChannel() throws RemoteException {
         NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW);
         final Notification n = new Notification.Builder(getContext())
                 .setContentTitle("foo")
@@ -101,7 +110,7 @@
     }
 
     @Test
-    public void testInvalidShortcutFlagEnabled_looksUpCorrectNonChannel() {
+    public void testInvalidShortcutFlagEnabled_looksUpCorrectNonChannel() throws RemoteException {
         NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW);
         final Notification n = new Notification.Builder(getContext())
                 .setContentTitle("foo")
@@ -122,7 +131,7 @@
     }
 
     @Test
-    public void testInvalidShortcutFlagDisabled_looksUpCorrectChannel() {
+    public void testInvalidShortcutFlagDisabled_looksUpCorrectChannel() throws RemoteException {
         NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW);
         final Notification n = new Notification.Builder(getContext())
                 .setContentTitle("foo")
@@ -143,7 +152,7 @@
 
     @Test
     @EnableFlags(Flags.FLAG_RESTRICT_AUDIO_ATTRIBUTES_CALL)
-    public void testAudioAttributes_callStyleCanUseCallUsage() {
+    public void testAudioAttributes_callStyleCanUseCallUsage() throws RemoteException {
         NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
         channel.setSound(Uri.EMPTY, new AudioAttributes.Builder()
                 .setUsage(USAGE_NOTIFICATION_RINGTONE)
@@ -162,11 +171,12 @@
         assertThat(mExtractor.process(r)).isNull();
         assertThat(r.getAudioAttributes().getUsage()).isEqualTo(USAGE_NOTIFICATION_RINGTONE);
         assertThat(r.getChannel()).isEqualTo(channel);
+        verify(mPlatformCompat, never()).reportChangeByUid(anyLong(), anyInt());
     }
 
     @Test
     @EnableFlags(Flags.FLAG_RESTRICT_AUDIO_ATTRIBUTES_CALL)
-    public void testAudioAttributes_nonCallStyleCannotUseCallUsage() {
+    public void testAudioAttributes_nonCallStyleCannotUseCallUsage() throws RemoteException {
         NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
         channel.setSound(Uri.EMPTY, new AudioAttributes.Builder()
                 .setUsage(USAGE_NOTIFICATION_RINGTONE)
@@ -180,13 +190,14 @@
         assertThat(mExtractor.process(r)).isNull();
         // instance updated
         assertThat(r.getAudioAttributes().getUsage()).isEqualTo(USAGE_NOTIFICATION);
+        verify(mPlatformCompat).reportChangeByUid(RESTRICT_AUDIO_ATTRIBUTES, r.getUid());
         // in-memory channel unchanged
         assertThat(channel.getAudioAttributes().getUsage()).isEqualTo(USAGE_NOTIFICATION_RINGTONE);
     }
 
     @Test
     @EnableFlags(Flags.FLAG_RESTRICT_AUDIO_ATTRIBUTES_ALARM)
-    public void testAudioAttributes_alarmCategoryCanUseAlarmUsage() {
+    public void testAudioAttributes_alarmCategoryCanUseAlarmUsage() throws RemoteException {
         NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
         channel.setSound(Uri.EMPTY, new AudioAttributes.Builder()
                 .setUsage(USAGE_ALARM)
@@ -201,11 +212,12 @@
         assertThat(mExtractor.process(r)).isNull();
         assertThat(r.getAudioAttributes().getUsage()).isEqualTo(USAGE_ALARM);
         assertThat(r.getChannel()).isEqualTo(channel);
+        verify(mPlatformCompat, never()).reportChangeByUid(anyLong(), anyInt());
     }
 
     @Test
     @EnableFlags(Flags.FLAG_RESTRICT_AUDIO_ATTRIBUTES_ALARM)
-    public void testAudioAttributes_nonAlarmCategoryCannotUseAlarmUsage() {
+    public void testAudioAttributes_nonAlarmCategoryCannotUseAlarmUsage() throws RemoteException {
         NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
         channel.setSound(Uri.EMPTY, new AudioAttributes.Builder()
                 .setUsage(USAGE_ALARM)
@@ -219,13 +231,14 @@
         assertThat(mExtractor.process(r)).isNull();
         // instance updated
         assertThat(r.getAudioAttributes().getUsage()).isEqualTo(USAGE_NOTIFICATION);
+        verify(mPlatformCompat).reportChangeByUid(RESTRICT_AUDIO_ATTRIBUTES, r.getUid());
         // in-memory channel unchanged
         assertThat(channel.getAudioAttributes().getUsage()).isEqualTo(USAGE_ALARM);
     }
 
     @Test
     @EnableFlags(Flags.FLAG_RESTRICT_AUDIO_ATTRIBUTES_MEDIA)
-    public void testAudioAttributes_noMediaUsage() {
+    public void testAudioAttributes_noMediaUsage() throws RemoteException {
         NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
         channel.setSound(Uri.EMPTY, new AudioAttributes.Builder()
                 .setUsage(USAGE_MEDIA)
@@ -239,13 +252,14 @@
         assertThat(mExtractor.process(r)).isNull();
         // instance updated
         assertThat(r.getAudioAttributes().getUsage()).isEqualTo(USAGE_NOTIFICATION);
+        verify(mPlatformCompat).reportChangeByUid(RESTRICT_AUDIO_ATTRIBUTES, r.getUid());
         // in-memory channel unchanged
         assertThat(channel.getAudioAttributes().getUsage()).isEqualTo(USAGE_MEDIA);
     }
 
     @Test
     @EnableFlags(Flags.FLAG_RESTRICT_AUDIO_ATTRIBUTES_MEDIA)
-    public void testAudioAttributes_noUnknownUsage() {
+    public void testAudioAttributes_noUnknownUsage() throws RemoteException {
         NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
         channel.setSound(Uri.EMPTY, new AudioAttributes.Builder()
                 .setUsage(USAGE_UNKNOWN)
@@ -259,6 +273,7 @@
         assertThat(mExtractor.process(r)).isNull();
         // instance updated
         assertThat(r.getAudioAttributes().getUsage()).isEqualTo(USAGE_NOTIFICATION);
+        verify(mPlatformCompat).reportChangeByUid(RESTRICT_AUDIO_ATTRIBUTES, r.getUid());
         // in-memory channel unchanged
         assertThat(channel.getAudioAttributes().getUsage()).isEqualTo(USAGE_UNKNOWN);
     }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 805bc17..011f2e3 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -200,6 +200,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.LauncherApps;
+import android.content.pm.ModuleInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ParceledListSlice;
@@ -450,6 +451,9 @@
 
     private static final String VALID_CONVO_SHORTCUT_ID = "shortcut";
     private static final String SEARCH_SELECTOR_PKG = "searchSelector";
+    private static final String ADSERVICES_MODULE_PKG = "com.android.adservices";
+    private static final String ADSERVICES_APK_PKG = "com.android.adservices.api";
+
     @Mock
     private NotificationListeners mListeners;
     @Mock
@@ -740,7 +744,11 @@
 
         // Return first true for RoleObserver main-thread check
         when(mMainLooper.isCurrentThread()).thenReturn(true).thenReturn(false);
-
+        ModuleInfo moduleInfo = new ModuleInfo();
+        moduleInfo.setApexModuleName(ADSERVICES_MODULE_PKG);
+        moduleInfo.setApkInApexPackageNames(List.of(ADSERVICES_APK_PKG));
+        when(mPackageManagerClient.getInstalledModules(anyInt()))
+                .thenReturn(List.of(moduleInfo));
         if (upToBootPhase >= SystemService.PHASE_SYSTEM_SERVICES_READY) {
             mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY, mMainLooper);
         }
@@ -13218,6 +13226,33 @@
     }
 
     @Test
+    public void fixSystemNotification_defaultAdservices_withOnGoingFlag_nondismissible()
+            throws Exception {
+        final ApplicationInfo ai = new ApplicationInfo();
+        ai.packageName = ADSERVICES_APK_PKG;
+        ai.uid = mUid;
+        ai.flags |= ApplicationInfo.FLAG_SYSTEM;
+
+        when(mPackageManagerClient.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
+                .thenReturn(ai);
+        when(mAppOpsManager.checkOpNoThrow(
+                AppOpsManager.OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS, ai.uid,
+                ai.packageName)).thenReturn(AppOpsManager.MODE_IGNORED);
+        // Given: a notification from an app on the system partition has the flag
+        // FLAG_ONGOING_EVENT set
+        Notification n = new Notification.Builder(mContext, "test")
+                .setOngoing(true)
+                .build();
+
+        // When: fix the notification with NotificationManagerService
+        mService.fixNotification(n, ADSERVICES_APK_PKG, "tag", 9, 0, mUid, NOT_FOREGROUND_SERVICE,
+                 true);
+
+        // Then: the notification's flag FLAG_NO_DISMISS should be set
+        assertNotSame(0, n.flags & Notification.FLAG_NO_DISMISS);
+    }
+
+    @Test
     public void fixCallNotification_withOnGoingFlag_shouldNotBeNonDismissible()
             throws Exception {
         // Given: a call notification has the flag FLAG_ONGOING_EVENT set
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index aeeca2ae..5033a380 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -3981,7 +3981,7 @@
         pm.applicationInfo = new ApplicationInfo();
         pm.applicationInfo.uid = UID_O;
         List<PackageInfo> packages = ImmutableList.of(pm);
-        when(mPm.getInstalledPackagesAsUser(any(), anyInt())).thenReturn(packages);
+        when(mPm.getInstalledPackagesAsUser(eq(0), anyInt())).thenReturn(packages);
         mHelper.updateFixedImportance(users);
 
         assertTrue(mHelper.isImportanceLocked(PKG_O, UID_O));
@@ -4097,7 +4097,7 @@
         pm.applicationInfo = new ApplicationInfo();
         pm.applicationInfo.uid = UID_O;
         List<PackageInfo> packages = ImmutableList.of(pm);
-        when(mPm.getInstalledPackagesAsUser(any(), eq(0))).thenReturn(packages);
+        when(mPm.getInstalledPackagesAsUser(0, 0)).thenReturn(packages);
         mHelper.updateFixedImportance(users);
 
         assertTrue(mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false)
@@ -4120,7 +4120,7 @@
         pm.applicationInfo = new ApplicationInfo();
         pm.applicationInfo.uid = UID_O;
         List<PackageInfo> packages = ImmutableList.of(pm);
-        when(mPm.getInstalledPackagesAsUser(any(), eq(0))).thenReturn(packages);
+        when(mPm.getInstalledPackagesAsUser(0, 0)).thenReturn(packages);
         mHelper.updateFixedImportance(users);
 
         NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
@@ -4309,7 +4309,7 @@
         pm.applicationInfo = new ApplicationInfo();
         pm.applicationInfo.uid = UID_O;
         List<PackageInfo> packages = ImmutableList.of(pm);
-        when(mPm.getInstalledPackagesAsUser(any(), eq(0))).thenReturn(packages);
+        when(mPm.getInstalledPackagesAsUser(0, 0)).thenReturn(packages);
         mHelper.updateFixedImportance(users);
 
         ArraySet<String> toRemove = new ArraySet<>();
@@ -4341,7 +4341,7 @@
         pm.applicationInfo = new ApplicationInfo();
         pm.applicationInfo.uid = UID_O;
         List<PackageInfo> packages = ImmutableList.of(pm);
-        when(mPm.getInstalledPackagesAsUser(any(), eq(0))).thenReturn(packages);
+        when(mPm.getInstalledPackagesAsUser(0, 0)).thenReturn(packages);
         mHelper.updateFixedImportance(users);
 
         assertTrue(mHelper.isImportanceLocked(PKG_O, UID_O));
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
index ad420f6..527001d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -55,6 +55,7 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.compat.IPlatformCompat;
 import com.android.server.UiServiceTestCase;
 
 import org.junit.Before;
@@ -155,7 +156,8 @@
                 NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
         when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
         mHelper = new RankingHelper(getContext(), mHandler, mConfig, mMockZenModeHelper,
-                mUsageStats, new String[] {ImportanceExtractor.class.getName()});
+                mUsageStats, new String[] {ImportanceExtractor.class.getName()},
+                mock(IPlatformCompat.class));
 
         mNotiGroupGSortA = new Notification.Builder(mContext, TEST_CHANNEL_ID)
                 .setContentTitle("A")
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 2c88ed2..7356b43 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1717,6 +1717,7 @@
         // The display should be rotated after the launch is finished.
         doReturn(false).when(app).isAnimating(anyInt(), anyInt());
         mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(app.token);
+        waitHandlerIdle(mWm.mH);
         mStatusBarWindow.finishSeamlessRotation(t);
         mNavBarWindow.finishSeamlessRotation(t);
 
diff --git a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
index 7b5b07c..f31a87f 100644
--- a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
+++ b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
@@ -349,15 +349,16 @@
     }
 
     /**
-     * @param plmn target plmn for validation.
-     * @return {@code true} if the target plmn is valid {@code false} otherwise.
+     * @param input string that want to be compared.
+     * @param regex string that express regular expression
+     * @return {@code true} if matched  {@code false} otherwise.
      */
-    public static boolean isValidPlmn(@Nullable String plmn) {
-        if (TextUtils.isEmpty(plmn)) {
+    private static boolean isValidPattern(@Nullable String input, @Nullable String regex) {
+        if (TextUtils.isEmpty(input) || TextUtils.isEmpty(regex)) {
             return false;
         }
-        Pattern pattern = Pattern.compile("^(?:[0-9]{3})(?:[0-9]{2}|[0-9]{3})$");
-        Matcher matcher = pattern.matcher(plmn);
+        Pattern pattern = Pattern.compile(regex);
+        Matcher matcher = pattern.matcher(input);
         if (!matcher.matches()) {
             return false;
         }
@@ -365,6 +366,22 @@
     }
 
     /**
+     * @param countryCode two letters country code based on the ISO 3166-1.
+     * @return {@code true} if the countryCode is valid {@code false} otherwise.
+     */
+    public static boolean isValidCountryCode(@Nullable String countryCode) {
+        return isValidPattern(countryCode, "^[A-Za-z]{2}$");
+    }
+
+    /**
+     * @param plmn target plmn for validation.
+     * @return {@code true} if the target plmn is valid {@code false} otherwise.
+     */
+    public static boolean isValidPlmn(@Nullable String plmn) {
+        return isValidPattern(plmn, "^(?:[0-9]{3})(?:[0-9]{2}|[0-9]{3})$");
+    }
+
+    /**
      * @param serviceType target serviceType for validation.
      * @return {@code true} if the target serviceType is valid {@code false} otherwise.
      */
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index ebabbf9..ca4a643 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -1039,17 +1039,18 @@
      * subscription on the
      * current eUICC and the subscription to be downloaded according to the subscription metadata.
      * Without the former, an {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} will be
-     * eturned in the callback intent to prompt the user to accept the download.
+     * returned in the callback intent to prompt the user to accept the download.
      *
      * <p> Starting from Android {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM},
      * if the caller has the
      * {@code android.Manifest.permission#MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS} permission or
-     * is a profile owner or device owner, and
-     * {@code switchAfterDownload} is {@code false}, then the downloaded subscription
-     * will be managed by that caller. If {@code switchAfterDownload} is true,
-     * an {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} will be
-     * returned in the callback intent to prompt the user to accept the download and the
-     * subscription will not be managed.
+     * is a profile owner or device owner, then the downloaded subscription
+     * will be managed by that caller.
+     * In case the caller is device owner or profile owner of an organization-owned device, {@code
+     * switchAfterDownload} can be set to true to automatically enable the subscription after
+     * download. If the caller is a profile owner on non organization owned device
+     * {@code switchAfterDownload} should be false otherwise the operation will fail with
+     * {@link #EMBEDDED_SUBSCRIPTION_RESULT_ERROR}.
      *
      * <p>On a multi-active SIM device, requires the
      * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission, or a calling app
diff --git a/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/TelephonyUtilsTest.java b/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/TelephonyUtilsTest.java
index 7558332..f88d82b 100644
--- a/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/TelephonyUtilsTest.java
+++ b/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/TelephonyUtilsTest.java
@@ -85,6 +85,8 @@
         assertTrue(TelephonyUtils.isValidPlmn("45006"));
         assertFalse(TelephonyUtils.isValidPlmn("1234567"));
         assertFalse(TelephonyUtils.isValidPlmn("1234"));
+        assertFalse(TelephonyUtils.isValidPlmn(""));
+        assertFalse(TelephonyUtils.isValidPlmn(null));
     }
 
     @Test
@@ -94,6 +96,19 @@
         assertFalse(TelephonyUtils.isValidService(FIRST_SERVICE_TYPE - 1));
         assertFalse(TelephonyUtils.isValidService(LAST_SERVICE_TYPE + 1));
     }
+
+    @Test
+    public void testIsValidCountryCode() {
+        assertTrue(TelephonyUtils.isValidCountryCode("US"));
+        assertTrue(TelephonyUtils.isValidCountryCode("cn"));
+        assertFalse(TelephonyUtils.isValidCountryCode("11"));
+        assertFalse(TelephonyUtils.isValidCountryCode("USA"));
+        assertFalse(TelephonyUtils.isValidCountryCode("chn"));
+        assertFalse(TelephonyUtils.isValidCountryCode("U"));
+        assertFalse(TelephonyUtils.isValidCountryCode("G7"));
+        assertFalse(TelephonyUtils.isValidCountryCode(""));
+        assertFalse(TelephonyUtils.isValidCountryCode(null));
+    }
 }
 
 
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java
index 0a83a53..c8b60e5 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java
@@ -17,8 +17,11 @@
 package com.android.server.vcn.routeselection;
 
 import static android.net.vcn.VcnManager.VCN_NETWORK_SELECTION_IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_KEY;
+import static android.net.vcn.VcnManager.VCN_NETWORK_SELECTION_MAX_SEQ_NUM_INCREASE_PER_SECOND_KEY;
 import static android.net.vcn.VcnManager.VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY;
 
+import static com.android.server.vcn.routeselection.IpSecPacketLossDetector.MIN_VALID_EXPECTED_RX_PACKET_NUM;
+import static com.android.server.vcn.routeselection.IpSecPacketLossDetector.getMaxSeqNumIncreasePerSecond;
 import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
 
 import static org.junit.Assert.assertEquals;
@@ -65,6 +68,7 @@
     private static final int REPLAY_BITMAP_LEN_BYTE = 512;
     private static final int REPLAY_BITMAP_LEN_BIT = REPLAY_BITMAP_LEN_BYTE * 8;
     private static final int IPSEC_PACKET_LOSS_PERCENT_THRESHOLD = 5;
+    private static final int MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED = -1;
     private static final long POLL_IPSEC_STATE_INTERVAL_MS = TimeUnit.SECONDS.toMillis(30L);
 
     @Mock private IpSecTransformWrapper mIpSecTransform;
@@ -91,6 +95,9 @@
                         eq(VCN_NETWORK_SELECTION_IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_KEY),
                         anyInt()))
                 .thenReturn(IPSEC_PACKET_LOSS_PERCENT_THRESHOLD);
+        when(mCarrierConfig.getInt(
+                        eq(VCN_NETWORK_SELECTION_MAX_SEQ_NUM_INCREASE_PER_SECOND_KEY), anyInt()))
+                .thenReturn(MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED);
 
         when(mDependencies.getPacketLossCalculator()).thenReturn(mPacketLossCalculator);
 
@@ -112,6 +119,20 @@
                 .build();
     }
 
+    private static IpSecTransformState newNextTransformState(
+            IpSecTransformState before,
+            long timeDiffMillis,
+            long rxSeqNoDiff,
+            long packtCountDiff,
+            int packetInWin) {
+        return new IpSecTransformState.Builder()
+                .setTimestampMillis(before.getTimestampMillis() + timeDiffMillis)
+                .setRxHighestSequenceNumber(before.getRxHighestSequenceNumber() + rxSeqNoDiff)
+                .setPacketCount(before.getPacketCount() + packtCountDiff)
+                .setReplayBitmap(newReplayBitmap(packetInWin))
+                .build();
+    }
+
     private static byte[] newReplayBitmap(int receivedPktCnt) {
         final BitSet bitSet = new BitSet(REPLAY_BITMAP_LEN_BIT);
         for (int i = 0; i < receivedPktCnt; i++) {
@@ -165,7 +186,7 @@
         // Verify the first polled state is stored
         assertEquals(mTransformStateInitial, mIpSecPacketLossDetector.getLastTransformState());
         verify(mPacketLossCalculator, never())
-                .getPacketLossRatePercentage(any(), any(), anyString());
+                .getPacketLossRatePercentage(any(), any(), anyInt(), anyString());
 
         // Verify next poll is scheduled
         assertNull(mTestLooper.nextMessage());
@@ -278,7 +299,7 @@
 
         xfrmStateReceiver.onResult(newTransformState(1, 1, newReplayBitmap(1)));
         verify(mPacketLossCalculator, never())
-                .getPacketLossRatePercentage(any(), any(), anyString());
+                .getPacketLossRatePercentage(any(), any(), anyInt(), anyString());
     }
 
     @Test
@@ -289,7 +310,7 @@
 
         xfrmStateReceiver.onError(new RuntimeException("Test"));
         verify(mPacketLossCalculator, never())
-                .getPacketLossRatePercentage(any(), any(), anyString());
+                .getPacketLossRatePercentage(any(), any(), anyInt(), anyString());
     }
 
     private void checkHandleLossRate(
@@ -301,7 +322,7 @@
                 startMonitorAndCaptureStateReceiver();
         doReturn(mockPacketLossRate)
                 .when(mPacketLossCalculator)
-                .getPacketLossRatePercentage(any(), any(), anyString());
+                .getPacketLossRatePercentage(any(), any(), anyInt(), anyString());
 
         // Mock receiving two states with mTransformStateInitial and an arbitrary transformNew
         final IpSecTransformState transformNew = newTransformState(1, 1, newReplayBitmap(1));
@@ -311,7 +332,10 @@
         // Verifications
         verify(mPacketLossCalculator)
                 .getPacketLossRatePercentage(
-                        eq(mTransformStateInitial), eq(transformNew), anyString());
+                        eq(mTransformStateInitial),
+                        eq(transformNew),
+                        eq(MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED),
+                        anyString());
 
         if (isLastStateExpectedToUpdate) {
             assertEquals(transformNew, mIpSecPacketLossDetector.getLastTransformState());
@@ -351,6 +375,22 @@
                 false /* isCallbackExpected */);
     }
 
+    @Test
+    public void testHandleLossRate_unusualSeqNumLeap_highLossRate() throws Exception {
+        checkHandleLossRate(
+                PacketLossCalculationResult.unusualSeqNumLeap(22),
+                true /* isLastStateExpectedToUpdate */,
+                false /* isCallbackExpected */);
+    }
+
+    @Test
+    public void testHandleLossRate_unusualSeqNumLeap_lowLossRate() throws Exception {
+        checkHandleLossRate(
+                PacketLossCalculationResult.unusualSeqNumLeap(2),
+                true /* isLastStateExpectedToUpdate */,
+                true /* isCallbackExpected */);
+    }
+
     private void checkGetPacketLossRate(
             IpSecTransformState oldState,
             IpSecTransformState newState,
@@ -358,7 +398,8 @@
             throws Exception {
         assertEquals(
                 expectedLossRate,
-                mPacketLossCalculator.getPacketLossRatePercentage(oldState, newState, TAG));
+                mPacketLossCalculator.getPacketLossRatePercentage(
+                        oldState, newState, MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED, TAG));
     }
 
     private void checkGetPacketLossRate(
@@ -397,6 +438,21 @@
     }
 
     @Test
+    public void testGetPacketLossRate_expectedPacketNumTooFew() throws Exception {
+        final int oldRxNo = 4096;
+        final int oldPktCnt = 4096;
+        final int pktCntDiff = MIN_VALID_EXPECTED_RX_PACKET_NUM - 1;
+        final byte[] bitmapReceiveAll = newReplayBitmap(4096);
+
+        final IpSecTransformState oldState =
+                newTransformState(oldRxNo, oldPktCnt, bitmapReceiveAll);
+        final IpSecTransformState newState =
+                newTransformState(oldRxNo + pktCntDiff, oldPktCnt + pktCntDiff, bitmapReceiveAll);
+
+        checkGetPacketLossRate(oldState, newState, PacketLossCalculationResult.invalid());
+    }
+
+    @Test
     public void testGetPacketLossRate_againstInitialState() throws Exception {
         checkGetPacketLossRate(mTransformStateInitial, 7000, 7001, 4096, 0);
         checkGetPacketLossRate(mTransformStateInitial, 7000, 6000, 4096, 15);
@@ -443,6 +499,45 @@
         checkGetPacketLossRate(oldState, 20000, 14000, 3000, 10);
     }
 
+    private void checkGetPktLossRate_unusualSeqNumLeap(
+            int maxSeqNumIncreasePerSecond,
+            int timeDiffMillis,
+            int rxSeqNoDiff,
+            PacketLossCalculationResult expected)
+            throws Exception {
+        final IpSecTransformState oldState = mTransformStateInitial;
+        final IpSecTransformState newState =
+                newNextTransformState(
+                        oldState,
+                        timeDiffMillis,
+                        rxSeqNoDiff,
+                        1 /* packtCountDiff */,
+                        1 /* packetInWin */);
+
+        assertEquals(
+                expected,
+                mPacketLossCalculator.getPacketLossRatePercentage(
+                        oldState, newState, maxSeqNumIncreasePerSecond, TAG));
+    }
+
+    @Test
+    public void testGetPktLossRate_unusualSeqNumLeap() throws Exception {
+        checkGetPktLossRate_unusualSeqNumLeap(
+                10000 /* maxSeqNumIncreasePerSecond */,
+                (int) TimeUnit.SECONDS.toMillis(2L),
+                30000 /* rxSeqNoDiff */,
+                PacketLossCalculationResult.unusualSeqNumLeap(100));
+    }
+
+    @Test
+    public void testGetPktLossRate_unusualSeqNumLeap_smallSeqNumDiff() throws Exception {
+        checkGetPktLossRate_unusualSeqNumLeap(
+                10000 /* maxSeqNumIncreasePerSecond */,
+                (int) TimeUnit.SECONDS.toMillis(2L),
+                5000 /* rxSeqNoDiff */,
+                PacketLossCalculationResult.valid(100));
+    }
+
     // Verify the polling event is scheduled with expected delays
     private void verifyPollEventDelayAndScheduleNext(long expectedDelayMs) {
         if (expectedDelayMs > 0) {
@@ -469,4 +564,24 @@
         // Verify the 3rd poll is scheduled with configured delay
         verifyPollEventDelayAndScheduleNext(POLL_IPSEC_STATE_INTERVAL_MS);
     }
+
+    @Test
+    public void testGetMaxSeqNumIncreasePerSecond() throws Exception {
+        final int seqNumLeapNegative = 500_000;
+        when(mCarrierConfig.getInt(
+                        eq(VCN_NETWORK_SELECTION_MAX_SEQ_NUM_INCREASE_PER_SECOND_KEY), anyInt()))
+                .thenReturn(seqNumLeapNegative);
+        assertEquals(seqNumLeapNegative, getMaxSeqNumIncreasePerSecond(mCarrierConfig));
+    }
+
+    @Test
+    public void testGetMaxSeqNumIncreasePerSecond_negativeValue() throws Exception {
+        final int seqNumLeapNegative = -10;
+        when(mCarrierConfig.getInt(
+                        eq(VCN_NETWORK_SELECTION_MAX_SEQ_NUM_INCREASE_PER_SECOND_KEY), anyInt()))
+                .thenReturn(seqNumLeapNegative);
+        assertEquals(
+                MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED,
+                getMaxSeqNumIncreasePerSecond(mCarrierConfig));
+    }
 }
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java
index af6daa1..6189fb0 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java
@@ -123,6 +123,7 @@
 
         mSetFlagsRule.enableFlags(Flags.FLAG_VALIDATE_NETWORK_ON_IPSEC_LOSS);
         mSetFlagsRule.enableFlags(Flags.FLAG_EVALUATE_IPSEC_LOSS_ON_LP_NC_CHANGE);
+        mSetFlagsRule.enableFlags(Flags.FLAG_HANDLE_SEQ_NUM_LEAP);
 
         when(mNetwork.getNetId()).thenReturn(-1);