Merge "Rename pixel_system_sw_touch to system_sw_touch" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index c231b30..9e30843 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -180,6 +180,12 @@
     ],
 }
 
+cc_aconfig_library {
+    name: "android_location_flags_c_lib",
+    vendor_available: true,
+    aconfig_declarations: "android.location.flags-aconfig",
+}
+
 java_aconfig_library {
     name: "android.location.flags-aconfig-java",
     aconfig_declarations: "android.location.flags-aconfig",
@@ -249,6 +255,13 @@
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
 
+java_aconfig_library {
+    name: "android.app.usage.flags-aconfig-java-host",
+    aconfig_declarations: "android.app.usage.flags-aconfig",
+    host_supported: true,
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
 // OS
 aconfig_declarations {
     name: "android.os.flags-aconfig",
diff --git a/core/api/current.txt b/core/api/current.txt
index 95af71c..5abb92b 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -12368,6 +12368,7 @@
 
   public final class ModuleInfo implements android.os.Parcelable {
     method public int describeContents();
+    method @FlaggedApi("android.content.pm.provide_info_of_apk_in_apex") @NonNull public java.util.Collection<java.lang.String> getApkInApexPackageNames();
     method @Nullable public CharSequence getName();
     method @Nullable public String getPackageName();
     method public boolean isHidden();
@@ -12378,6 +12379,7 @@
   public class PackageInfo implements android.os.Parcelable {
     ctor public PackageInfo();
     method public int describeContents();
+    method @FlaggedApi("android.content.pm.provide_info_of_apk_in_apex") @Nullable public String getApexPackageName();
     method @FlaggedApi("android.content.pm.archiving") public long getArchiveTimeMillis();
     method public long getLongVersionCode();
     method public void setLongVersionCode(long);
@@ -53956,8 +53958,10 @@
     field public static final String PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH = "android.window.PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH";
     field public static final String PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE = "android.window.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE";
     field public static final String PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE = "android.window.PROPERTY_COMPAT_ALLOW_DISPLAY_ORIENTATION_OVERRIDE";
+    field @FlaggedApi("com.android.window.flags.app_compat_properties_api") public static final String PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED = "android.window.PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED";
     field @FlaggedApi("com.android.window.flags.app_compat_properties_api") public static final String PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE = "android.window.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE";
     field public static final String PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE = "android.window.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE";
+    field @FlaggedApi("com.android.window.flags.app_compat_properties_api") public static final String PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES = "android.window.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES";
     field public static final String PROPERTY_COMPAT_ALLOW_SANDBOXING_VIEW_BOUNDS_APIS = "android.window.PROPERTY_COMPAT_ALLOW_SANDBOXING_VIEW_BOUNDS_APIS";
     field public static final String PROPERTY_COMPAT_ENABLE_FAKE_FOCUS = "android.window.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS";
     field public static final String PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION = "android.window.PROPERTY_COMPAT_IGNORE_REQUESTED_ORIENTATION";
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index ad8b685..572be19 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1010,6 +1010,7 @@
     field public static final long OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH = 264304459L; // 0xfc0f74bL
     field public static final long OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE = 264301586L; // 0xfc0ec12L
     field public static final long OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS = 263259275L; // 0xfb1048bL
+    field @FlaggedApi("com.android.window.flags.app_compat_properties_api") public static final long OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED = 273509367L; // 0x104d6bf7L
     field public static final long OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION = 254631730L; // 0xf2d5f32L
     field public static final long OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE = 266124927L; // 0xfdcbe7fL
     field public static final long OVERRIDE_MIN_ASPECT_RATIO = 174042980L; // 0xa5faf64L
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 183b9b0..7c52238 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -6971,16 +6971,21 @@
     public static final int FLAG_DEBUG_LOG_RESOLUTION = 0x00000008;
     /**
      * If set, this intent will not match any components in packages that
-     * are currently stopped.  If this is not set, then the default behavior
-     * is to include such applications in the result.
+     * are currently
+     * {@linkplain android.content.pm.ApplicationInfo#FLAG_STOPPED stopped}.
+     * If this is not set, then the default behavior is to include such
+     * applications in the result.
      */
     public static final int FLAG_EXCLUDE_STOPPED_PACKAGES = 0x00000010;
     /**
      * If set, this intent will always match any components in packages that
-     * are currently stopped.  This is the default behavior when
+     * are currently
+     * {@linkplain android.content.pm.ApplicationInfo#FLAG_STOPPED stopped}.
+     * This is the default behavior when
      * {@link #FLAG_EXCLUDE_STOPPED_PACKAGES} is not set.  If both of these
      * flags are set, this one wins (it allows overriding of exclude for
-     * places where the framework may automatically set the exclude flag).
+     * places where the framework may automatically set the exclude flag,
+     * such as broadcasts).
      */
     public static final int FLAG_INCLUDE_STOPPED_PACKAGES = 0x00000020;
 
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 12da665..30871e9 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -16,6 +16,7 @@
 
 package android.content.pm;
 
+import android.annotation.FlaggedApi;
 import android.annotation.FloatRange;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -42,6 +43,7 @@
 import android.window.OnBackInvokedCallback;
 
 import com.android.internal.util.Parcelling;
+import com.android.window.flags.Flags;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -1099,6 +1101,8 @@
     @ChangeId
     @Overridable
     @Disabled
+    @TestApi
+    @FlaggedApi(Flags.FLAG_APP_COMPAT_PROPERTIES_API)
     public static final long OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED =
             273509367L; // buganizer id
 
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 3713380..869c621 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -375,6 +375,19 @@
     /**
      * Value for {@link #flags}: true if this application's package is in
      * the stopped state.
+     *
+     * <p>Stopped is the initial state after an app is installed, before it is launched
+     * or otherwise directly interacted with by the user. The system tries not to
+     * start it unless initiated by a user interaction (typically launching its icon
+     * from the launcher, could also include user actions like adding it as an app widget,
+     * selecting it as a live wallpaper, selecting it as a keyboard, etc). Stopped
+     * applications will not receive broadcasts unless the sender specifies
+     * {@link android.content.Intent#FLAG_INCLUDE_STOPPED_PACKAGES}.
+     *
+     * <p>Applications should avoid launching activies, binding to or starting services, or
+     * otherwise causing a stopped application to run unless initiated by the user.
+     *
+     * <p>An app can also return to the stopped state by a "force stop".
      */
     public static final int FLAG_STOPPED = 1<<21;
 
diff --git a/core/java/android/content/pm/ModuleInfo.java b/core/java/android/content/pm/ModuleInfo.java
index a7306a3..a1c8747 100644
--- a/core/java/android/content/pm/ModuleInfo.java
+++ b/core/java/android/content/pm/ModuleInfo.java
@@ -16,10 +16,15 @@
 
 package android.content.pm;
 
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -46,6 +51,13 @@
     /** Whether or not this module is hidden from the user. */
     private boolean mHidden;
 
+    /**
+     * The list of the package names of all APK-in-APEX apps in the module, or
+     * null if there are none.
+     */
+    @Nullable
+    private List<String> mApkInApexPackageNames;
+
     // TODO: Decide whether we need an additional metadata bundle to support out of band
     // updates to ModuleInfo.
     //
@@ -61,6 +73,9 @@
         mPackageName = orig.mPackageName;
         mHidden = orig.mHidden;
         mApexModuleName = orig.mApexModuleName;
+        if (orig.mApkInApexPackageNames != null) {
+            mApkInApexPackageNames = List.copyOf(orig.mApkInApexPackageNames);
+        }
     }
 
     /** @hide Sets the public name of this module. */
@@ -107,6 +122,25 @@
         return mApexModuleName;
     }
 
+    /** @hide Sets the list of the package name of APK-in-APEX apps in this module. */
+    public ModuleInfo setApkInApexPackageNames(@NonNull Collection<String> apkInApexPackageNames) {
+        Objects.requireNonNull(apkInApexPackageNames);
+        mApkInApexPackageNames = List.copyOf(apkInApexPackageNames);
+        return this;
+    }
+
+    /**
+     * Gets the list of the package name of all APK-in-APEX apps in the module.
+     */
+    @NonNull
+    @FlaggedApi(android.content.pm.Flags.FLAG_PROVIDE_INFO_OF_APK_IN_APEX)
+    public Collection<String> getApkInApexPackageNames() {
+        if (mApkInApexPackageNames == null) {
+            return Collections.emptyList();
+        }
+        return mApkInApexPackageNames;
+    }
+
     /** Returns a string representation of this object. */
     public String toString() {
         return "ModuleInfo{"
@@ -125,6 +159,7 @@
         hashCode = 31 * hashCode + Objects.hashCode(mName);
         hashCode = 31 * hashCode + Objects.hashCode(mPackageName);
         hashCode = 31 * hashCode + Objects.hashCode(mApexModuleName);
+        hashCode = 31 * hashCode + Objects.hashCode(mApkInApexPackageNames);
         hashCode = 31 * hashCode + Boolean.hashCode(mHidden);
         return hashCode;
     }
@@ -138,6 +173,7 @@
         return Objects.equals(mName, other.mName)
                 && Objects.equals(mPackageName, other.mPackageName)
                 && Objects.equals(mApexModuleName, other.mApexModuleName)
+                && Objects.equals(mApkInApexPackageNames, other.mApkInApexPackageNames)
                 && mHidden == other.mHidden;
     }
 
@@ -147,6 +183,8 @@
         dest.writeString(mPackageName);
         dest.writeBoolean(mHidden);
         dest.writeString(mApexModuleName);
+        // Parcel#writeStringList handles null case, we can use it directly
+        dest.writeStringList(mApkInApexPackageNames);
     }
 
     private ModuleInfo(Parcel source) {
@@ -154,6 +192,7 @@
         mPackageName = source.readString();
         mHidden = source.readBoolean();
         mApexModuleName = source.readString();
+        mApkInApexPackageNames = source.createStringArrayList();
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<ModuleInfo> CREATOR =
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 4f61613..c1c9928 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -499,6 +499,16 @@
      */
     public boolean isActiveApex;
 
+    /**
+     * If the package is an APEX package (i.e. the value of {@link #isApex}
+     * is true), this field is the package name of the APEX. If the package
+     * is one APK-in-APEX app, this field is the package name of the parent
+     * APEX that contains the app. If the package is not one of the above
+     * two cases, this field is {@code null}.
+     */
+    @Nullable
+    private String mApexPackageName;
+
     public PackageInfo() {
     }
 
@@ -535,6 +545,26 @@
         mArchiveTimeMillis = value;
     }
 
+    /**
+     * If the package is an APEX package (i.e. the value of {@link #isApex}
+     * is true), returns the package name of the APEX. If the package
+     * is one APK-in-APEX app, returns the package name of the parent
+     * APEX that contains the app. If the package is not one of the above
+     * two cases, returns {@code null}.
+     */
+    @Nullable
+    @FlaggedApi(android.content.pm.Flags.FLAG_PROVIDE_INFO_OF_APK_IN_APEX)
+    public String getApexPackageName() {
+        return mApexPackageName;
+    }
+
+    /**
+     * @hide
+     */
+    public void setApexPackageName(@Nullable String apexPackageName) {
+        mApexPackageName = apexPackageName;
+    }
+
     @Override
     public String toString() {
         return "PackageInfo{"
@@ -603,6 +633,12 @@
         dest.writeBoolean(isApex);
         dest.writeBoolean(isActiveApex);
         dest.writeLong(mArchiveTimeMillis);
+        if (mApexPackageName != null) {
+            dest.writeInt(1);
+            dest.writeString8(mApexPackageName);
+        } else {
+            dest.writeInt(0);
+        }
         dest.restoreAllowSquashing(prevAllowSquashing);
     }
 
@@ -669,5 +705,9 @@
         isApex = source.readBoolean();
         isActiveApex = source.readBoolean();
         mArchiveTimeMillis = source.readLong();
+        int hasApexPackageName = source.readInt();
+        if (hasApexPackageName != 0) {
+            mApexPackageName = source.readString8();
+        }
     }
 }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 8151a91..0fb0993 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -11024,7 +11024,7 @@
     }
 
     /**
-     * Returns the property defined in the given package's &lt;appliction&gt; tag.
+     * Returns the property defined in the given package's &lt;application&gt; tag.
      *
      * @throws NameNotFoundException if either the given package is not installed or if the
      * given property is not defined within the &lt;application&gt; tag.
diff --git a/core/java/android/content/rollback/OWNERS b/core/java/android/content/rollback/OWNERS
index 3093fd6..8e5a0d8 100644
--- a/core/java/android/content/rollback/OWNERS
+++ b/core/java/android/content/rollback/OWNERS
@@ -1,5 +1,5 @@
-# Bug component: 557916
+# Bug component: 819107
 
-narayan@google.com
-nandana@google.com
-olilan@google.com
+ancr@google.com
+harshitmahajan@google.com
+robertogil@google.com
diff --git a/core/java/android/credentials/ui/Constants.java b/core/java/android/credentials/ui/Constants.java
index 7092f29..37f850b 100644
--- a/core/java/android/credentials/ui/Constants.java
+++ b/core/java/android/credentials/ui/Constants.java
@@ -29,6 +29,13 @@
     public static final String EXTRA_RESULT_RECEIVER =
             "android.credentials.ui.extra.RESULT_RECEIVER";
 
+    /**
+     * The intent extra key for indicating whether the bottom sheet should be started directly
+     * on the 'All Options' screen.
+     */
+    public static final String EXTRA_REQ_FOR_ALL_OPTIONS =
+            "android.credentials.ui.extra.REQ_FOR_ALL_OPTIONS";
+
     /** The intent action for when the enabled Credential Manager providers has been updated. */
     public static final String CREDMAN_ENABLED_PROVIDERS_UPDATED =
             "android.credentials.ui.action.CREDMAN_ENABLED_PROVIDERS_UPDATED";
diff --git a/core/java/android/credentials/ui/IntentFactory.java b/core/java/android/credentials/ui/IntentFactory.java
index 5e8372d..49321d5 100644
--- a/core/java/android/credentials/ui/IntentFactory.java
+++ b/core/java/android/credentials/ui/IntentFactory.java
@@ -35,6 +35,31 @@
  */
 @TestApi
 public class IntentFactory {
+
+    /**
+     * Generate a new launch intent to the Credential Selector UI.
+     *
+     * @hide
+     */
+    @NonNull
+    public static Intent createCredentialSelectorIntent(
+            @NonNull RequestInfo requestInfo,
+            @SuppressLint("ConcreteCollection") // Concrete collection needed for marshalling.
+            @NonNull
+            ArrayList<ProviderData> enabledProviderDataList,
+            @SuppressLint("ConcreteCollection") // Concrete collection needed for marshalling.
+            @NonNull
+            ArrayList<DisabledProviderData> disabledProviderDataList,
+            @NonNull ResultReceiver resultReceiver,
+            boolean isRequestForAllOptions) {
+
+        Intent intent = createCredentialSelectorIntent(requestInfo, enabledProviderDataList,
+                disabledProviderDataList, resultReceiver);
+        intent.putExtra(Constants.EXTRA_REQ_FOR_ALL_OPTIONS, isRequestForAllOptions);
+
+        return intent;
+    }
+
     /** Generate a new launch intent to the Credential Selector UI. */
     @NonNull
     public static Intent createCredentialSelectorIntent(
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 6626baf..7bea9ae 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -25,6 +25,7 @@
 import android.hardware.input.IInputDeviceBatteryState;
 import android.hardware.input.IKeyboardBacklightListener;
 import android.hardware.input.IKeyboardBacklightState;
+import android.hardware.input.IStickyModifierStateListener;
 import android.hardware.input.ITabletModeChangedListener;
 import android.hardware.input.TouchCalibration;
 import android.os.CombinedVibration;
@@ -241,4 +242,14 @@
     void unregisterKeyboardBacklightListener(IKeyboardBacklightListener listener);
 
     HostUsiVersion getHostUsiVersionFromDisplayConfig(int displayId);
+
+    @EnforcePermission("MONITOR_STICKY_MODIFIER_STATE")
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+            + "android.Manifest.permission.MONITOR_STICKY_MODIFIER_STATE)")
+    void registerStickyModifierStateListener(IStickyModifierStateListener listener);
+
+    @EnforcePermission("MONITOR_STICKY_MODIFIER_STATE")
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+            + "android.Manifest.permission.MONITOR_STICKY_MODIFIER_STATE)")
+    void unregisterStickyModifierStateListener(IStickyModifierStateListener listener);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt b/core/java/android/hardware/input/IStickyModifierStateListener.aidl
similarity index 61%
copy from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
copy to core/java/android/hardware/input/IStickyModifierStateListener.aidl
index efc7431..bd139ab 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
+++ b/core/java/android/hardware/input/IStickyModifierStateListener.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright 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.
@@ -14,12 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.di.bound
+package android.hardware.input;
 
-import javax.inject.Qualifier
+/** @hide */
+oneway interface IStickyModifierStateListener {
 
-/** User associated with current custom tile binding. */
-@Qualifier
-@MustBeDocumented
-@Retention(AnnotationRetention.RUNTIME)
-annotation class CustomTileUser
+    /**
+     * Called when the sticky modifier state is changed when A11y Sticky keys feature is enabled
+     */
+    void onStickyModifierStateChanged(int modifierState, int lockedModifierState);
+}
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index f941ad8..4ebbde7 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -1297,6 +1297,42 @@
     }
 
     /**
+     * Registers a Sticky modifier state change listener to be notified about {@link
+     * StickyModifierState} changes.
+     *
+     * @param executor an executor on which the callback will be called
+     * @param listener the {@link StickyModifierStateListener}
+     * @throws IllegalArgumentException if {@code listener} has already been registered previously.
+     * @throws NullPointerException     if {@code listener} or {@code executor} is null.
+     * @hide
+     * @see #unregisterStickyModifierStateListener(StickyModifierStateListener)
+     */
+    @RequiresPermission(Manifest.permission.MONITOR_STICKY_MODIFIER_STATE)
+    public void registerStickyModifierStateListener(@NonNull Executor executor,
+            @NonNull StickyModifierStateListener listener) throws IllegalArgumentException {
+        if (!InputSettings.isAccessibilityStickyKeysFeatureEnabled()) {
+            return;
+        }
+        mGlobal.registerStickyModifierStateListener(executor, listener);
+    }
+
+    /**
+     * Unregisters a previously added Sticky modifier state change listener.
+     *
+     * @param listener the {@link StickyModifierStateListener}
+     * @hide
+     * @see #registerStickyModifierStateListener(Executor, StickyModifierStateListener)
+     */
+    @RequiresPermission(Manifest.permission.MONITOR_STICKY_MODIFIER_STATE)
+    public void unregisterStickyModifierStateListener(
+            @NonNull StickyModifierStateListener listener) {
+        if (!InputSettings.isAccessibilityStickyKeysFeatureEnabled()) {
+            return;
+        }
+        mGlobal.unregisterStickyModifierStateListener(listener);
+    }
+
+    /**
      * A callback used to be notified about battery state changes for an input device. The
      * {@link #onBatteryStateChanged(int, long, BatteryState)} method will be called once after the
      * listener is successfully registered to provide the initial battery state of the device.
@@ -1378,4 +1414,23 @@
         void onKeyboardBacklightChanged(
                 int deviceId, @NonNull KeyboardBacklightState state, boolean isTriggeredByKeyPress);
     }
+
+    /**
+     * A callback used to be notified about sticky modifier state changes when A11y Sticky keys
+     * feature is enabled.
+     *
+     * @see #registerStickyModifierStateListener(Executor, StickyModifierStateListener)
+     * @see #unregisterStickyModifierStateListener(StickyModifierStateListener)
+     * @hide
+     */
+    public interface StickyModifierStateListener {
+        /**
+         * Called when the sticky modifier state changes.
+         * This method will be called once after the listener is successfully registered to provide
+         * the initial modifier state.
+         *
+         * @param state the new sticky modifier state, never null.
+         */
+        void onStickyModifierStateChanged(@NonNull StickyModifierState state);
+    }
 }
diff --git a/core/java/android/hardware/input/InputManagerGlobal.java b/core/java/android/hardware/input/InputManagerGlobal.java
index 24a6911..7c104a0 100644
--- a/core/java/android/hardware/input/InputManagerGlobal.java
+++ b/core/java/android/hardware/input/InputManagerGlobal.java
@@ -27,6 +27,7 @@
 import android.hardware.input.InputManager.InputDeviceListener;
 import android.hardware.input.InputManager.KeyboardBacklightListener;
 import android.hardware.input.InputManager.OnTabletModeChangedListener;
+import android.hardware.input.InputManager.StickyModifierStateListener;
 import android.hardware.lights.Light;
 import android.hardware.lights.LightState;
 import android.hardware.lights.LightsManager;
@@ -52,6 +53,7 @@
 import android.view.InputEvent;
 import android.view.InputMonitor;
 import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
 import android.view.PointerIcon;
 
 import com.android.internal.annotations.GuardedBy;
@@ -100,6 +102,14 @@
     @GuardedBy("mKeyboardBacklightListenerLock")
     @Nullable private IKeyboardBacklightListener mKeyboardBacklightListener;
 
+    private final Object mStickyModifierStateListenerLock = new Object();
+    @GuardedBy("mStickyModifierStateListenerLock")
+    @Nullable
+    private ArrayList<StickyModifierStateListenerDelegate> mStickyModifierStateListeners;
+    @GuardedBy("mStickyModifierStateListenerLock")
+    @Nullable
+    private IStickyModifierStateListener mStickyModifierStateListener;
+
     // InputDeviceSensorManager gets notified synchronously from the binder thread when input
     // devices change, so it must be synchronized with the input device listeners.
     @GuardedBy("mInputDeviceListeners")
@@ -905,6 +915,158 @@
         }
     }
 
+    private static final class StickyModifierStateListenerDelegate {
+        final InputManager.StickyModifierStateListener mListener;
+        final Executor mExecutor;
+
+        StickyModifierStateListenerDelegate(StickyModifierStateListener listener,
+                Executor executor) {
+            mListener = listener;
+            mExecutor = executor;
+        }
+
+        void notifyStickyModifierStateChange(int modifierState, int lockedModifierState) {
+            mExecutor.execute(() ->
+                    mListener.onStickyModifierStateChanged(
+                            new LocalStickyModifierState(modifierState, lockedModifierState)));
+        }
+    }
+
+    private class LocalStickyModifierStateListener extends IStickyModifierStateListener.Stub {
+
+        @Override
+        public void onStickyModifierStateChanged(int modifierState, int lockedModifierState) {
+            synchronized (mStickyModifierStateListenerLock) {
+                if (mStickyModifierStateListeners == null) return;
+                final int numListeners = mStickyModifierStateListeners.size();
+                for (int i = 0; i < numListeners; i++) {
+                    mStickyModifierStateListeners.get(i)
+                            .notifyStickyModifierStateChange(modifierState, lockedModifierState);
+                }
+            }
+        }
+    }
+
+    // Implementation of the android.hardware.input.StickyModifierState interface used to report
+    // the sticky modifier state via the StickyModifierStateListener interfaces.
+    private static final class LocalStickyModifierState extends StickyModifierState {
+
+        private final int mModifierState;
+        private final int mLockedModifierState;
+
+        LocalStickyModifierState(int modifierState, int lockedModifierState) {
+            mModifierState = modifierState;
+            mLockedModifierState = lockedModifierState;
+        }
+
+        @Override
+        public boolean isShiftModifierOn() {
+            return (mModifierState & KeyEvent.META_SHIFT_ON) != 0;
+        }
+
+        @Override
+        public boolean isShiftModifierLocked() {
+            return (mLockedModifierState & KeyEvent.META_SHIFT_ON) != 0;
+        }
+
+        @Override
+        public boolean isCtrlModifierOn() {
+            return (mModifierState & KeyEvent.META_CTRL_ON) != 0;
+        }
+
+        @Override
+        public boolean isCtrlModifierLocked() {
+            return (mLockedModifierState & KeyEvent.META_CTRL_ON) != 0;
+        }
+
+        @Override
+        public boolean isMetaModifierOn() {
+            return (mModifierState & KeyEvent.META_META_ON) != 0;
+        }
+
+        @Override
+        public boolean isMetaModifierLocked() {
+            return (mLockedModifierState & KeyEvent.META_META_ON) != 0;
+        }
+
+        @Override
+        public boolean isAltModifierOn() {
+            return (mModifierState & KeyEvent.META_ALT_LEFT_ON) != 0;
+        }
+
+        @Override
+        public boolean isAltModifierLocked() {
+            return (mLockedModifierState & KeyEvent.META_ALT_LEFT_ON) != 0;
+        }
+
+        @Override
+        public boolean isAltGrModifierOn() {
+            return (mModifierState & KeyEvent.META_ALT_RIGHT_ON) != 0;
+        }
+
+        @Override
+        public boolean isAltGrModifierLocked() {
+            return (mLockedModifierState & KeyEvent.META_ALT_RIGHT_ON) != 0;
+        }
+    }
+
+    /**
+     * @see InputManager#registerStickyModifierStateListener(Executor, StickyModifierStateListener)
+     */
+    @RequiresPermission(Manifest.permission.MONITOR_STICKY_MODIFIER_STATE)
+    void registerStickyModifierStateListener(@NonNull Executor executor,
+            @NonNull StickyModifierStateListener listener) throws IllegalArgumentException {
+        Objects.requireNonNull(executor, "executor should not be null");
+        Objects.requireNonNull(listener, "listener should not be null");
+
+        synchronized (mStickyModifierStateListenerLock) {
+            if (mStickyModifierStateListener == null) {
+                mStickyModifierStateListeners = new ArrayList<>();
+                mStickyModifierStateListener = new LocalStickyModifierStateListener();
+
+                try {
+                    mIm.registerStickyModifierStateListener(mStickyModifierStateListener);
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
+                }
+            }
+            final int numListeners = mStickyModifierStateListeners.size();
+            for (int i = 0; i < numListeners; i++) {
+                if (mStickyModifierStateListeners.get(i).mListener == listener) {
+                    throw new IllegalArgumentException("Listener has already been registered!");
+                }
+            }
+            StickyModifierStateListenerDelegate delegate =
+                    new StickyModifierStateListenerDelegate(listener, executor);
+            mStickyModifierStateListeners.add(delegate);
+        }
+    }
+
+    /**
+     * @see InputManager#unregisterStickyModifierStateListener(StickyModifierStateListener)
+     */
+    @RequiresPermission(Manifest.permission.MONITOR_STICKY_MODIFIER_STATE)
+    void unregisterStickyModifierStateListener(
+            @NonNull StickyModifierStateListener listener) {
+        Objects.requireNonNull(listener, "listener should not be null");
+
+        synchronized (mStickyModifierStateListenerLock) {
+            if (mStickyModifierStateListeners == null) {
+                return;
+            }
+            mStickyModifierStateListeners.removeIf((delegate) -> delegate.mListener == listener);
+            if (mStickyModifierStateListeners.isEmpty()) {
+                try {
+                    mIm.unregisterStickyModifierStateListener(mStickyModifierStateListener);
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
+                }
+                mStickyModifierStateListeners = null;
+                mStickyModifierStateListener = null;
+            }
+        }
+    }
+
     /**
      * @see InputManager#getKeyboardLayoutsForInputDevice(InputDeviceIdentifier)
      */
diff --git a/core/java/android/hardware/input/StickyModifierState.java b/core/java/android/hardware/input/StickyModifierState.java
new file mode 100644
index 0000000..a3f7a0a
--- /dev/null
+++ b/core/java/android/hardware/input/StickyModifierState.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 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 android.hardware.input;
+
+/**
+ * The StickyModifierState class is a representation of a modifier state when A11y Sticky keys
+ * feature is enabled
+ *
+ * @hide
+ */
+public abstract class StickyModifierState {
+
+    /**
+     * Represents whether current sticky modifier state includes 'Shift' modifier.
+     * <p> If {@code true} the next {@link android.view.KeyEvent} will contain 'Shift' modifier in
+     * its metaState.
+     *
+     * @return whether Shift modifier key is on.
+     */
+    public abstract boolean isShiftModifierOn();
+
+    /**
+     * Represents whether current sticky modifier state includes 'Shift' modifier, and it is
+     * locked.
+     * <p> If {@code true} any subsequent {@link android.view.KeyEvent} will contain 'Shift'
+     * modifier in its metaState and this state will remain sticky (will not be cleared), until
+     * user presses 'Shift' key again to clear the locked state.
+     *
+     * @return whether Shift modifier key is locked.
+     */
+    public abstract boolean isShiftModifierLocked();
+
+    /**
+     * Represents whether current sticky modifier state includes 'Ctrl' modifier.
+     * <p> If {@code true} the next {@link android.view.KeyEvent} will contain 'Ctrl' modifier in
+     * its metaState.
+     *
+     * @return whether Ctrl modifier key is on.
+     */
+    public abstract boolean isCtrlModifierOn();
+
+    /**
+     * Represents whether current sticky modifier state includes 'Ctrl' modifier, and it is
+     * locked.
+     * <p> If {@code true} any subsequent {@link android.view.KeyEvent} will contain 'Ctrl'
+     * modifier in its metaState and this state will remain sticky (will not be cleared), until
+     * user presses 'Ctrl' key again to clear the locked state.
+     *
+     * @return whether Ctrl modifier key is locked.
+     */
+    public abstract boolean isCtrlModifierLocked();
+
+    /**
+     * Represents whether current sticky modifier state includes 'Meta' modifier.
+     * <p> If {@code true} the next {@link android.view.KeyEvent} will contain 'Meta' modifier in
+     * its metaState.
+     *
+     * @return whether Meta modifier key is on.
+     */
+    public abstract boolean isMetaModifierOn();
+
+    /**
+     * Represents whether current sticky modifier state includes 'Meta' modifier, and it is
+     * locked.
+     * <p> If {@code true} any subsequent {@link android.view.KeyEvent} will contain 'Meta'
+     * modifier in its metaState and this state will remain sticky (will not be cleared), until
+     * user presses 'Meta' key again to clear the locked state.
+     *
+     * @return whether Meta modifier key is locked.
+     */
+    public abstract boolean isMetaModifierLocked();
+
+    /**
+     * Represents whether current sticky modifier state includes 'Alt' modifier.
+     * <p> If {@code true} the next {@link android.view.KeyEvent} will contain 'Alt' modifier in
+     * its metaState.
+     *
+     * @return whether Alt modifier key is on.
+     */
+    public abstract boolean isAltModifierOn();
+
+    /**
+     * Represents whether current sticky modifier state includes 'Alt' modifier, and it is
+     * locked.
+     * <p> If {@code true} any subsequent {@link android.view.KeyEvent} will contain 'Alt'
+     * modifier in its metaState and this state will remain sticky (will not be cleared), until
+     * user presses 'Alt' key again to clear the locked state.
+     *
+     * @return whether Alt modifier key is locked.
+     */
+    public abstract boolean isAltModifierLocked();
+
+    /**
+     * Represents whether current sticky modifier state includes 'AltGr' modifier.
+     * <p> If {@code true} the next {@link android.view.KeyEvent} will contain 'AltGr' modifier in
+     * its metaState.
+     *
+     * @return whether AltGr modifier key is on.
+     */
+    public abstract boolean isAltGrModifierOn();
+
+    /**
+     * Represents whether current sticky modifier state includes 'AltGr' modifier, and it is
+     * locked.
+     * <p> If {@code true} any subsequent {@link android.view.KeyEvent} will contain 'AltGr'
+     * modifier in its metaState and this state will remain sticky (will not be cleared), until
+     * user presses 'AltGr' key again to clear the locked state.
+     *
+     * @return whether AltGr modifier key is locked.
+     */
+    public abstract boolean isAltGrModifierLocked();
+}
+
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 8fcff78..3149de4 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -673,6 +673,7 @@
         if (anglePkg.isEmpty()) {
             return;
         }
+        intent.setPackage(anglePkg);
 
         context.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
             @Override
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index e32a8f3..8c8af0e 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -2373,13 +2373,29 @@
     /** Assume locked until we hear otherwise */
     private static volatile boolean sCeStorageUnlocked = false;
 
+    /**
+     * Avoid (potentially) costly and repeated lookups to the same mount service.
+     * Note that we don't use the Singleton wrapper as lookup may fail early during boot.
+     */
+    private static volatile IStorageManager sStorageManager;
+
     private static boolean isCeStorageUnlocked(int userId) {
-        final IStorageManager storage = IStorageManager.Stub
+        IStorageManager storage = sStorageManager;
+        if (storage == null) {
+            storage = IStorageManager.Stub
                 .asInterface(ServiceManager.getService("mount"));
+            // As the queried handle may be null early during boot, only stash valid handles,
+            // avoiding races with concurrent service queries.
+            if (storage != null) {
+                sStorageManager = storage;
+            }
+        }
         if (storage != null) {
             try {
                 return storage.isCeStorageUnlocked(userId);
             } catch (RemoteException ignored) {
+                // Conservatively clear the ref, allowing refresh if the remote process restarts.
+                sStorageManager = null;
             }
         }
         return false;
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 54116a2..692dad4 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -26,6 +26,8 @@
 import static android.view.View.SYSTEM_UI_FLAG_VISIBLE;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 
+import static com.android.window.flags.Flags.noConsecutiveVisibilityEvents;
+
 import android.animation.AnimationHandler;
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -1431,27 +1433,36 @@
                         }
 
                         if (didSurface && !mReportedVisible) {
-                            // This wallpaper is currently invisible, but its
-                            // surface has changed.  At this point let's tell it
-                            // again that it is invisible in case the report about
-                            // the surface caused it to start running.  We really
-                            // don't want wallpapers running when not visible.
                             if (mIsCreating) {
-                                // Some wallpapers will ignore this call if they
-                                // had previously been told they were invisble,
-                                // so if we are creating a new surface then toggle
-                                // the state to get them to notice.
-                                if (DEBUG) Log.v(TAG, "onVisibilityChanged(true) at surface: "
-                                        + this);
-                                Trace.beginSection("WPMS.Engine.onVisibilityChanged-true");
-                                onVisibilityChanged(true);
+                                // The surface has been created, but the wallpaper isn't visible.
+                                // Trigger onVisibilityChanged(true) then onVisibilityChanged(false)
+                                // to make sure the wallpaper is stopped even after the events
+                                // onSurfaceCreated() and onSurfaceChanged().
+                                if (noConsecutiveVisibilityEvents()) {
+                                    if (DEBUG) Log.v(TAG, "toggling doVisibilityChanged");
+                                    Trace.beginSection("WPMS.Engine.doVisibilityChanged-true");
+                                    doVisibilityChanged(true);
+                                    Trace.endSection();
+                                    Trace.beginSection("WPMS.Engine.doVisibilityChanged-false");
+                                    doVisibilityChanged(false);
+                                    Trace.endSection();
+                                } else {
+                                    if (DEBUG) {
+                                        Log.v(TAG, "onVisibilityChanged(true) at surface: " + this);
+                                    }
+                                    Trace.beginSection("WPMS.Engine.onVisibilityChanged-true");
+                                    onVisibilityChanged(true);
+                                    Trace.endSection();
+                                }
+                            }
+                            if (!noConsecutiveVisibilityEvents()) {
+                                if (DEBUG) {
+                                    Log.v(TAG, "onVisibilityChanged(false) at surface: " + this);
+                                }
+                                Trace.beginSection("WPMS.Engine.onVisibilityChanged-false");
+                                onVisibilityChanged(false);
                                 Trace.endSection();
                             }
-                            if (DEBUG) Log.v(TAG, "onVisibilityChanged(false) at surface: "
-                                        + this);
-                            Trace.beginSection("WPMS.Engine.onVisibilityChanged-false");
-                            onVisibilityChanged(false);
-                            Trace.endSection();
                         }
                     } finally {
                         mIsCreating = false;
diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java
index 19e6836..9c430cd 100644
--- a/core/java/android/view/InputEventReceiver.java
+++ b/core/java/android/view/InputEventReceiver.java
@@ -54,6 +54,7 @@
             InputChannel inputChannel, MessageQueue messageQueue);
     private static native void nativeDispose(long receiverPtr);
     private static native void nativeFinishInputEvent(long receiverPtr, int seq, boolean handled);
+    private static native boolean nativeProbablyHasInput(long receiverPtr);
     private static native void nativeReportTimeline(long receiverPtr, int inputEventId,
             long gpuCompletedTime, long presentTime);
     private static native boolean nativeConsumeBatchedInputEvents(long receiverPtr,
@@ -92,6 +93,17 @@
     }
 
     /**
+     * Checks the receiver for input availability.
+     * May return false negatives.
+     */
+    public boolean probablyHasInput() {
+        if (mReceiverPtr == 0) {
+            return false;
+        }
+        return nativeProbablyHasInput(mReceiverPtr);
+    }
+
+    /**
      * Disposes the receiver.
      * Must be called on the same Looper thread to which the receiver is attached.
      */
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 442ea66..2b99e1e 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -32,6 +32,7 @@
 import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH_ERROR_CODE;
 import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY;
 import static android.view.flags.Flags.FLAG_VIEW_VELOCITY_API;
+import static android.view.flags.Flags.enableUseMeasureCacheDuringForceLayout;
 import static android.view.flags.Flags.toolkitMetricsForFrameRateDecision;
 import static android.view.flags.Flags.toolkitSetFrameRateReadOnly;
 import static android.view.flags.Flags.viewVelocityApi;
@@ -955,6 +956,12 @@
     private static boolean sAlwaysRemeasureExactly = false;
 
     /**
+     * When true makes it possible to use onMeasure caches also when the force layout flag is
+     * enabled. This helps avoiding multiple measures in the same frame with the same dimensions.
+     */
+    private static boolean sUseMeasureCacheDuringForceLayoutFlagValue;
+
+    /**
      * Allow setForeground/setBackground to be called (and ignored) on a textureview,
      * without throwing
      */
@@ -2396,6 +2403,7 @@
 
         sToolkitSetFrameRateReadOnlyFlagValue = toolkitSetFrameRateReadOnly();
         sToolkitMetricsForFrameRateDecisionFlagValue = toolkitMetricsForFrameRateDecision();
+        sUseMeasureCacheDuringForceLayoutFlagValue = enableUseMeasureCacheDuringForceLayout();
     }
 
     /**
@@ -22848,6 +22856,36 @@
     }
 
     /**
+     * Determines whether an unprocessed input event is available on the window.
+     *
+     * This is only a performance hint (a.k.a. the Input Hint) and may return false negative
+     * results.  Callers should not rely on availability of the input event based on the return
+     * value of this method.
+     *
+     * The Input Hint functionality is experimental, and can be removed in the future OS releases.
+     *
+     * This method only returns nontrivial results on a View that is attached to a Window. Such View
+     * can be acquired using `Activity.getWindow().getDecorView()`, and only after the view
+     * hierarchy is attached (via {@link android.app.Activity#setContentView(android.view.View)}).
+     *
+     * In multi-window mode the View can provide the Input Hint only for the window it is attached
+     * to. Therefore, checking input availability for the whole application would require asking
+     * for the hint from more than one View.
+     *
+     * The initial implementation does not return false positives, but callers should not rely on
+     * it: false positives may occur in future OS releases.
+     *
+     * @hide
+     */
+    public boolean probablyHasInput() {
+        ViewRootImpl viewRootImpl = getViewRootImpl();
+        if (viewRootImpl == null) {
+            return false;
+        }
+        return viewRootImpl.probablyHasInput();
+    }
+
+    /**
      * Destroys all hardware rendering resources. This method is invoked
      * when the system needs to reclaim resources. Upon execution of this
      * method, you should free any OpenGL resources created by the view.
@@ -27417,7 +27455,13 @@
 
             resolveRtlPropertiesIfNeeded();
 
-            int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
+            int cacheIndex;
+            if (sUseMeasureCacheDuringForceLayoutFlagValue) {
+                cacheIndex =  mMeasureCache.indexOfKey(key);
+            } else {
+                cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
+            }
+
             if (cacheIndex < 0 || sIgnoreMeasureCache) {
                 if (isTraversalTracingEnabled()) {
                     Trace.beginSection(mTracingStrings.onMeasure);
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 287c7b2..fbefbf3 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -49,6 +49,7 @@
 import android.os.Bundle;
 import android.os.Parcelable;
 import android.os.SystemClock;
+import android.service.autofill.Flags;
 import android.util.AttributeSet;
 import android.util.IntArray;
 import android.util.Log;
@@ -3752,7 +3753,16 @@
                         && !child.isActivityDeniedForAutofillForUnimportantView())
                     || (shouldIncludeAllChildrenViewWithAutofillTypeNotNone(afm)
                         && child.getAutofillType() != AUTOFILL_TYPE_NONE)
-                    || shouldIncludeAllChildrenViews(afm)){
+                    || shouldIncludeAllChildrenViews(afm)
+                    || (Flags.includeInvisibleViewGroupInAssistStructure()
+                    && child instanceof ViewGroup && child.getVisibility() != View.VISIBLE)) {
+                // If the child is a ViewGroup object and its visibility is not visible, include
+                // it as part of the assist structure. The children of these invisible ViewGroup
+                // objects are parsed and included in the assist structure. When the Autofill
+                // Provider determines the visibility of these children, it looks at their
+                // visibility as well as their parent's visibility. Omitting invisible parents
+                // will lead to the Autofill Provider incorrectly assuming that these children
+                // of invisible parents are actually visible.
                 list.add(child);
             } else if (child instanceof ViewGroup) {
                 ((ViewGroup) child).populateChildrenForAutofill(list, flags);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 32afe06..8529b4e 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -10554,6 +10554,18 @@
     }
 
     /**
+     * Checks the input event receiver for input availability.
+     * May return false negatives.
+     * @hide
+     */
+    public boolean probablyHasInput() {
+        if (mInputEventReceiver == null) {
+            return false;
+        }
+        return mInputEventReceiver.probablyHasInput();
+    }
+
+    /**
      * Adds a scroll capture callback to this window.
      *
      * @param callback the callback to add
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index f76822f..61cf126 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -972,10 +972,8 @@
      *     android:value="false"/&gt;
      * &lt;/application&gt;
      * </pre>
-     *
-     * @hide
      */
-    // TODO(b/274924641): Make this public API.
+    @FlaggedApi(Flags.FLAG_APP_COMPAT_PROPERTIES_API)
     String PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED =
             "android.window.PROPERTY_COMPAT_ALLOW_IGNORING_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED";
 
@@ -1309,9 +1307,8 @@
      *     android:value="true|false"/&gt;
      * &lt;/application&gt;
      * </pre>
-     * @hide
      */
-    // TODO(b/280052089): Make this public API.
+    @FlaggedApi(Flags.FLAG_APP_COMPAT_PROPERTIES_API)
     String PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES =
             "android.window.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES";
 
diff --git a/core/java/android/view/flags/view_flags.aconfig b/core/java/android/view/flags/view_flags.aconfig
new file mode 100644
index 0000000..a74b06a
--- /dev/null
+++ b/core/java/android/view/flags/view_flags.aconfig
@@ -0,0 +1,10 @@
+package: "android.view.flags"
+
+flag {
+    name: "enable_use_measure_cache_during_force_layout"
+    namespace: "toolkit"
+    description: "Enables using the measure cache during a view force layout from the second "
+      "onMeasure call onwards during the same traversal."
+    bug: "316170253"
+    is_fixed_read_only: true
+}
diff --git a/core/java/android/window/flags/wallpaper_manager.aconfig b/core/java/android/window/flags/wallpaper_manager.aconfig
index f03c993..ea9da96 100644
--- a/core/java/android/window/flags/wallpaper_manager.aconfig
+++ b/core/java/android/window/flags/wallpaper_manager.aconfig
@@ -13,3 +13,10 @@
   description: "Support storing different wallpaper crops for different display dimensions. Only effective after rebooting."
   bug: "281648899"
 }
+
+flag {
+  name: "no_consecutive_visibility_events"
+  namespace: "systemui"
+  description: "Prevent the system from sending consecutive onVisibilityChanged(false) events."
+  bug: "285631818"
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambda.java b/core/java/com/android/internal/util/function/pooled/PooledLambda.java
index bdc8a66..1d6d69c 100755
--- a/core/java/com/android/internal/util/function/pooled/PooledLambda.java
+++ b/core/java/com/android/internal/util/function/pooled/PooledLambda.java
@@ -1009,7 +1009,7 @@
             K arg11, L arg12) {
         synchronized (Message.sPoolSync) {
             PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
-                    function, 11, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7,
+                    function, 12, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7,
                     arg8, arg9, arg10, arg11, arg12);
             return Message.obtain().setCallback(callback.recycleOnUse());
         }
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index 5b68e8e..f7d8152 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -82,6 +82,7 @@
     status_t initialize();
     void dispose();
     status_t finishInputEvent(uint32_t seq, bool handled);
+    bool probablyHasInput();
     status_t reportTimeline(int32_t inputEventId, nsecs_t gpuCompletedTime, nsecs_t presentTime);
     status_t consumeEvents(JNIEnv* env, bool consumeBatches, nsecs_t frameTime,
             bool* outConsumedBatch);
@@ -165,6 +166,10 @@
     return processOutboundEvents();
 }
 
+bool NativeInputEventReceiver::probablyHasInput() {
+    return mInputConsumer.probablyHasInput();
+}
+
 status_t NativeInputEventReceiver::reportTimeline(int32_t inputEventId, nsecs_t gpuCompletedTime,
                                                   nsecs_t presentTime) {
     if (kDebugDispatchCycle) {
@@ -547,6 +552,12 @@
     }
 }
 
+static bool nativeProbablyHasInput(JNIEnv* env, jclass clazz, jlong receiverPtr) {
+    sp<NativeInputEventReceiver> receiver =
+            reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
+    return receiver->probablyHasInput();
+}
+
 static void nativeReportTimeline(JNIEnv* env, jclass clazz, jlong receiverPtr, jint inputEventId,
                                  jlong gpuCompletedTime, jlong presentTime) {
     if (IdGenerator::getSource(inputEventId) != IdGenerator::Source::INPUT_READER) {
@@ -597,6 +608,7 @@
          (void*)nativeInit},
         {"nativeDispose", "(J)V", (void*)nativeDispose},
         {"nativeFinishInputEvent", "(JIZ)V", (void*)nativeFinishInputEvent},
+        {"nativeProbablyHasInput", "(J)Z", (void*)nativeProbablyHasInput},
         {"nativeReportTimeline", "(JIJJ)V", (void*)nativeReportTimeline},
         {"nativeConsumeBatchedInputEvents", "(JJ)Z", (void*)nativeConsumeBatchedInputEvents},
         {"nativeDump", "(JLjava/lang/String;)Ljava/lang/String;", (void*)nativeDump},
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 2a0feee..52e0124 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -395,6 +395,8 @@
     optional bool should_refresh_activity_for_camera_compat = 40;
     optional bool should_refresh_activity_via_pause_for_camera_compat = 41;
     optional bool should_override_min_aspect_ratio = 42;
+    optional bool should_ignore_orientation_request_loop = 43;
+    optional bool should_override_force_resize_app = 44;
 }
 
 /* represents WindowToken */
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 232a36f..e65bfab 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -7692,6 +7692,13 @@
     <permission android:name="android.permission.MONITOR_KEYBOARD_BACKLIGHT"
                 android:protectionLevel="signature" />
 
+    <!-- Allows low-level access to monitor sticky modifier state changes when A11Y Sticky keys
+         feature is enabled.
+         <p>Not for use by third-party applications.
+         @hide -->
+    <permission android:name="android.permission.MONITOR_STICKY_MODIFIER_STATE"
+                android:protectionLevel="signature" />
+
     <uses-permission android:name="android.permission.HANDLE_QUERY_PACKAGE_RESTART" />
 
     <!-- Allows financed device kiosk apps to perform actions on the Device Lock service
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 70f0c93..0cc49a5 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -2316,7 +2316,7 @@
     <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"إزالة الحظر"</string>
     <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"الخصوصية في جهاز الاستشعار"</string>
     <string name="splash_screen_view_icon_description" msgid="180638751260598187">"رمز التطبيق"</string>
-    <string name="splash_screen_view_branding_description" msgid="7911129347402728216">"الصورة الذهنية للعلامة التجارية للتطبيق"</string>
+    <string name="splash_screen_view_branding_description" msgid="7911129347402728216">"هوية العلامة التجارية للتطبيق"</string>
     <string name="view_and_control_notification_title" msgid="4300765399209912240">"التحقّق من إعدادات الوصول"</string>
     <string name="view_and_control_notification_content" msgid="8003766498562604034">"يمكن لخدمة <xliff:g id="SERVICE_NAME">%s</xliff:g> الاطّلاع على شاشتك والتحكّم فيها. انقر لمراجعة الإعدادات."</string>
     <string name="ui_translation_accessibility_translated_text" msgid="3197547218178944544">"<xliff:g id="MESSAGE">%1$s</xliff:g> (مُترجَم)."</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index a4b01e8..ec14677 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -2340,9 +2340,9 @@
     <string name="permdesc_startForegroundServicesFromBackground" msgid="4071826571656001537">"Permet que una aplicació complementària iniciï serveis en primer pla des d\'un segon pla."</string>
     <string name="mic_access_on_toast" msgid="2666925317663845156">"El micròfon està disponible"</string>
     <string name="mic_access_off_toast" msgid="8111040892954242437">"El micròfon està bloquejat"</string>
-    <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"No es pot projectar a la pantalla"</string>
+    <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"No es pot duplicar a la pantalla"</string>
     <string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Utilitza un altre cable i torna-ho a provar"</string>
-    <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"El dispositiu està massa calent i no pot projectar a la pantalla fins que es refredi"</string>
+    <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"El dispositiu està massa calent i no pot duplicar a la pantalla fins que es refredi"</string>
     <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Pantalla dual"</string>
     <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"La pantalla dual està activada"</string>
     <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"<xliff:g id="APP_NAME">%1$s</xliff:g> està utilitzant les dues pantalles per mostrar contingut"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 7119131..d3f8550 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -2050,7 +2050,7 @@
     <string name="autofill_save_type_password" msgid="5624528786144539944">"adgangskode"</string>
     <string name="autofill_save_type_address" msgid="3111006395818252885">"adresse"</string>
     <string name="autofill_save_type_credit_card" msgid="3583795235862046693">"kreditkort"</string>
-    <string name="autofill_save_type_debit_card" msgid="3169397504133097468">"betalingskort"</string>
+    <string name="autofill_save_type_debit_card" msgid="3169397504133097468">"debetkort"</string>
     <string name="autofill_save_type_payment_card" msgid="6555012156728690856">"betalingskort"</string>
     <string name="autofill_save_type_generic_card" msgid="1019367283921448608">"kort"</string>
     <string name="autofill_save_type_username" msgid="1018816929884640882">"brugernavn"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index c0e12c3..514d695 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -355,7 +355,7 @@
     <string name="permlab_fullScreenIntent" msgid="4310888199502509104">"Benachrichtigungen auf einem gesperrten Gerät als Vollbildaktivitäten anzeigen"</string>
     <string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"Ermöglicht der App, Benachrichtigungen auf einem gesperrten Gerät als Vollbildaktivitäten anzuzeigen"</string>
     <string name="permlab_install_shortcut" msgid="7451554307502256221">"Verknüpfungen installieren"</string>
-    <string name="permdesc_install_shortcut" msgid="4476328467240212503">"ohne Zutun des Nutzers Verknüpfungen zum Startbildschirm hinzufügen."</string>
+    <string name="permdesc_install_shortcut" msgid="4476328467240212503">"Ermöglicht einer App, dem Startbildschirm ohne Zutun des Nutzers Verknüpfungen hinzuzufügen."</string>
     <string name="permlab_uninstall_shortcut" msgid="295263654781900390">"Verknüpfungen deinstallieren"</string>
     <string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"Ermöglicht einer App das Entfernen von Verknüpfungen vom Startbildschirm ohne Eingriff des Nutzers"</string>
     <string name="permlab_processOutgoingCalls" msgid="4075056020714266558">"Ausgehende Anrufe umleiten"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index cf18eb9..2b9c555 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -2331,7 +2331,7 @@
     <string name="default_card_name" msgid="9198284935962911468">"<xliff:g id="CARDNUMBER">%d</xliff:g> TXARTELA"</string>
     <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Aplikazio osagarrien erloju-profilaren baimena erlojuak kudeatzeko"</string>
     <string name="permdesc_companionProfileWatch" msgid="5655698581110449397">"Erlojuak kudeatzeko baimena ematen die aplikazio osagarriei."</string>
-    <string name="permlab_observeCompanionDevicePresence" msgid="9008994909653990465">"Begiratu gailu osagarrien presentzia"</string>
+    <string name="permlab_observeCompanionDevicePresence" msgid="9008994909653990465">"Begiratu gailu osagarrien presentziari"</string>
     <string name="permdesc_observeCompanionDevicePresence" msgid="3011699826788697852">"Gailu osagarrien presentzia begiratzeko baimena ematen die aplikazio osagarriei gailuak inguruan edo urrun daudenean."</string>
     <string name="permlab_deliverCompanionMessages" msgid="3931552294842980887">"Entregatu aplikazio osagarrien mezuak"</string>
     <string name="permdesc_deliverCompanionMessages" msgid="2170847384281412850">"Beste gailuetan mezuak entregatzeko baimena ematen die aplikazio osagarriei."</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index d435c62..3b24230 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1724,7 +1724,7 @@
     <string name="color_inversion_feature_name" msgid="2672824491933264951">"Inversion des couleurs"</string>
     <string name="color_correction_feature_name" msgid="7975133554160979214">"Correction des couleurs"</string>
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Mode une main"</string>
-    <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Encore moins lumineux"</string>
+    <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Luminosité ultra-réduite"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Appareils auditifs"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Touches de volume appuyées de manière prolongée. Service <xliff:g id="SERVICE_NAME">%1$s</xliff:g> activé."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Touches de volume appuyées de manière prolongée. Service <xliff:g id="SERVICE_NAME">%1$s</xliff:g> désactivé."</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 2ff302d..9a063fb 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -575,7 +575,7 @@
     <string name="permdesc_changeWimaxState" product="tablet" msgid="4011097664859480108">"Inaruhusu programu kuunganisha kompyuta kibao,  na kukata kompyuta kibao kutoka mitandao ya WiMAX."</string>
     <string name="permdesc_changeWimaxState" product="tv" msgid="5373274458799425276">"Huruhusu programu iunganishe na kutenganisha kifaa chako cha Android TV na mitandao ya WiMAX."</string>
     <string name="permdesc_changeWimaxState" product="default" msgid="1551666203780202101">"Inaruhusu programu kuunganisha simu kwenye, na kukata simu kutoka mitandao ya WiMAX."</string>
-    <string name="permlab_bluetooth" msgid="586333280736937209">"oanisha na vifaa vya Bluetooth"</string>
+    <string name="permlab_bluetooth" msgid="586333280736937209">"unganisha na vifaa vya Bluetooth"</string>
     <string name="permdesc_bluetooth" product="tablet" msgid="3053222571491402635">"Huruhusu programu kuona usanidi wa Bluetooth kwenye kompyuta kibao, na kutuma na kukubali miunganisho kwa vifaa vilivyooanishwa."</string>
     <string name="permdesc_bluetooth" product="tv" msgid="8851534496561034998">"Huruhusu programu iangalie mipangilio iliyowekwa ya Bluetooth kwenye kifaa chako cha Android TV na kufanya na kukubali miunganisho na vifaa vilivyooanishwa."</string>
     <string name="permdesc_bluetooth" product="default" msgid="2779606714091276746">"Huruhusu programu kuona usanidi wa Bluetooth kwenye simu, na kutuma na kukubali miunganisho kwa vifaa vilivyooanishwa."</string>
diff --git a/core/tests/coretests/src/android/content/pm/ModuleInfoTest.java b/core/tests/coretests/src/android/content/pm/ModuleInfoTest.java
new file mode 100644
index 0000000..4366e02c
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/ModuleInfoTest.java
@@ -0,0 +1,99 @@
+/*
+ * 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 android.content.pm;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+import android.platform.test.annotations.AppModeFull;
+import android.text.TextUtils;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+@AppModeFull
+public class ModuleInfoTest {
+
+    private static final String APEX_MODULE_NAME = "apexModuleName";
+    private static final String APK_IN_APEX_PACKAGE_NAME = "apkInApexPackageName";
+    private static final String MODULE_PACKAGE_NAME = "modulePackageName";
+    private static final String MODULE_NAME = "moduleName";
+
+    @Test
+    public void testSimple() {
+        ModuleInfo info = new ModuleInfo();
+        assertThat(info.toString()).isNotNull();
+    }
+
+    @Test
+    public void testDefaultCopy() {
+        ModuleInfo oldInfo = new ModuleInfo();
+        ModuleInfo newInfo = new ModuleInfo(oldInfo);
+        assertThat(newInfo).isEqualTo(oldInfo);
+    }
+
+    @Test
+    public void testCopy() {
+        boolean isHidden = false;
+        ModuleInfo info = new ModuleInfo();
+        info.setHidden(isHidden);
+        info.setApexModuleName(APEX_MODULE_NAME);
+        info.setPackageName(MODULE_PACKAGE_NAME);
+        info.setName(MODULE_NAME);
+        info.setApkInApexPackageNames(List.of(APK_IN_APEX_PACKAGE_NAME));
+
+        ModuleInfo newInfo = new ModuleInfo(info);
+        assertThat(newInfo).isEqualTo(info);
+    }
+
+    @Test
+    public void testGetApkInApexPackageNamesReturnEmptyListInDefault() {
+        ModuleInfo info = new ModuleInfo();
+        assertThat(info.getApkInApexPackageNames()).isNotNull();
+        assertThat(info.getApkInApexPackageNames()).isEmpty();
+    }
+
+    @Test
+    public void testModuleInfoParcelizeDeparcelize() {
+        boolean isHidden = false;
+        ModuleInfo info = new ModuleInfo();
+        info.setHidden(isHidden);
+        info.setApexModuleName(APEX_MODULE_NAME);
+        info.setPackageName(MODULE_PACKAGE_NAME);
+        info.setName(MODULE_NAME);
+        info.setApkInApexPackageNames(List.of(APK_IN_APEX_PACKAGE_NAME));
+
+        final Parcel p = Parcel.obtain();
+        info.writeToParcel(p, 0);
+        p.setDataPosition(0);
+
+        final ModuleInfo targetInfo = ModuleInfo.CREATOR.createFromParcel(p);
+        p.recycle();
+
+        assertThat(info.isHidden()).isEqualTo(targetInfo.isHidden());
+        assertThat(info.getApexModuleName()).isEqualTo(targetInfo.getApexModuleName());
+        assertThat(info.getPackageName()).isEqualTo(targetInfo.getPackageName());
+        assertThat(TextUtils.equals(info.getName(), targetInfo.getName())).isTrue();
+        assertThat(info.getApkInApexPackageNames().toArray()).isEqualTo(
+                targetInfo.getApkInApexPackageNames().toArray());
+    }
+}
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml
index cec7ee2..ef7478c 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml
@@ -18,13 +18,13 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/desktop_mode_caption"
-    android:layout_width="match_parent"
+    android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:gravity="center_horizontal">
 
     <ImageButton
         android:id="@+id/caption_handle"
-        android:layout_width="128dp"
+        android:layout_width="@dimen/desktop_mode_fullscreen_decor_caption_width"
         android:layout_height="@dimen/desktop_mode_fullscreen_decor_caption_height"
         android:paddingVertical="16dp"
         android:contentDescription="@string/handle_text"
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 0a40cea..28e7098 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -413,6 +413,9 @@
     <!-- Height of desktop mode caption for fullscreen tasks. -->
     <dimen name="desktop_mode_fullscreen_decor_caption_height">36dp</dimen>
 
+    <!-- Width of desktop mode caption for fullscreen tasks. -->
+    <dimen name="desktop_mode_fullscreen_decor_caption_width">128dp</dimen>
+
     <!-- Required empty space to be visible for partially offscreen tasks. -->
     <dimen name="freeform_required_visible_empty_space_in_header">48dp</dimen>
 
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 4fd3625..61a8e9b 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
@@ -726,7 +726,7 @@
     private void handleEventOutsideFocusedCaption(MotionEvent ev,
             DesktopModeWindowDecoration relevantDecor) {
         // Returns if event occurs within caption
-        if (relevantDecor == null || relevantDecor.checkTouchEventInCaption(ev)) {
+        if (relevantDecor == null || relevantDecor.checkTouchEventInCaptionHandle(ev)) {
             return;
         }
 
@@ -761,7 +761,8 @@
                                 || windowingMode == WINDOWING_MODE_MULTI_WINDOW;
                     }
 
-                    if (dragFromStatusBarAllowed && relevantDecor.checkTouchEventInHandle(ev)) {
+                    if (dragFromStatusBarAllowed
+                            && relevantDecor.checkTouchEventInCaptionHandle(ev)) {
                         mTransitionDragActive = true;
                     }
                 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 0c8e93b..d08b655 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -317,6 +317,7 @@
         relayoutParams.mLayoutResId =
             getDesktopModeWindowDecorLayoutId(taskInfo.getWindowingMode());
         relayoutParams.mCaptionHeightId = getCaptionHeightIdStatic(taskInfo.getWindowingMode());
+        relayoutParams.mCaptionWidthId = getCaptionWidthId(relayoutParams.mLayoutResId);
         if (DesktopModeStatus.useWindowShadow(/* isFocusedWindow= */ taskInfo.isFocused)) {
             relayoutParams.mShadowRadiusId = taskInfo.isFocused
                     ? R.dimen.freeform_decor_shadow_focused_thickness
@@ -345,6 +346,17 @@
         }
     }
 
+    /**
+     * If task has focused window decor, return the caption id of the fullscreen caption size
+     * resource. Otherwise, return ID_NULL and caption width be set to task width.
+     */
+    private static int getCaptionWidthId(int layoutResId) {
+        if (layoutResId == R.layout.desktop_mode_focused_window_decor) {
+            return R.dimen.desktop_mode_fullscreen_decor_caption_width;
+        }
+        return Resources.ID_NULL;
+    }
+
 
     private PointF calculateMaximizeMenuPosition() {
         final PointF position = new PointF();
@@ -558,7 +570,6 @@
                 .setOnClickListener(mOnCaptionButtonClickListener)
                 .setOnTouchListener(mOnCaptionTouchListener)
                 .setLayoutId(mRelayoutParams.mLayoutResId)
-                .setCaptionPosition(mRelayoutParams.mCaptionX, mRelayoutParams.mCaptionY)
                 .setWindowingButtonsVisible(DesktopModeStatus.isEnabled())
                 .setCaptionHeight(mResult.mCaptionHeight)
                 .build();
@@ -635,35 +646,25 @@
                 mTaskOrganizer.getRunningTaskInfo(mTaskInfo.taskId);
         if (taskInfo == null) return result;
         final Point positionInParent = taskInfo.positionInParent;
-        result.offset(-mRelayoutParams.mCaptionX, -mRelayoutParams.mCaptionY);
         result.offset(-positionInParent.x, -positionInParent.y);
         return result;
     }
 
     /**
-     * Determine if a passed MotionEvent is in a view in caption
+     * Checks if motion event occurs in the caption handle area. This should be used in cases where
+     * onTouchListener will not work (i.e. when caption is in status bar area).
      *
      * @param ev       the {@link MotionEvent} to check
-     * @param layoutId the id of the view
      * @return {@code true} if event is inside the specified view, {@code false} if not
      */
-    private boolean checkEventInCaptionView(MotionEvent ev, int layoutId) {
-        if (mResult.mRootView == null) return false;
+    boolean checkTouchEventInCaptionHandle(MotionEvent ev) {
+        if (isHandleMenuActive() || !(mWindowDecorViewHolder
+                instanceof DesktopModeFocusedWindowDecorationViewHolder)) {
+            return false;
+        }
         final PointF inputPoint = offsetCaptionLocation(ev);
-        final View view = mResult.mRootView.findViewById(layoutId);
-        return view != null && pointInView(view, inputPoint.x, inputPoint.y);
-    }
-
-    boolean checkTouchEventInHandle(MotionEvent ev) {
-        if (isHandleMenuActive()) return false;
-        return checkEventInCaptionView(ev, R.id.caption_handle);
-    }
-
-    /**
-     * Returns true if motion event is within the caption's root view's bounds.
-     */
-    boolean checkTouchEventInCaption(MotionEvent ev) {
-        return checkEventInCaptionView(ev, getCaptionViewId());
+        return ((DesktopModeFocusedWindowDecorationViewHolder) mWindowDecorViewHolder)
+                .pointInCaption(inputPoint, mResult.mCaptionX);
     }
 
     /**
@@ -676,24 +677,19 @@
     void checkClickEvent(MotionEvent ev) {
         if (mResult.mRootView == null) return;
         if (!isHandleMenuActive()) {
+            // Click if point in caption handle view
             final View caption = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
             final View handle = caption.findViewById(R.id.caption_handle);
-            clickIfPointInView(new PointF(ev.getX(), ev.getY()), handle);
+            if (checkTouchEventInCaptionHandle(ev)) {
+                mOnCaptionButtonClickListener.onClick(handle);
+            }
         } else {
             mHandleMenu.checkClickEvent(ev);
             closeHandleMenuIfNeeded(ev);
         }
     }
 
-    private boolean clickIfPointInView(PointF inputPoint, View v) {
-        if (pointInView(v, inputPoint.x, inputPoint.y)) {
-            mOnCaptionButtonClickListener.onClick(v);
-            return true;
-        }
-        return false;
-    }
-
-    boolean pointInView(View v, float x, float y) {
+    private boolean pointInView(View v, float x, float y) {
         return v != null && v.getLeft() <= x && v.getRight() >= x
                 && v.getTop() <= y && v.getBottom() >= y;
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
index 652a2ed..b37dd0d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
@@ -64,8 +64,6 @@
     private final View.OnTouchListener mOnTouchListener;
     private final RunningTaskInfo mTaskInfo;
     private final int mLayoutResId;
-    private final int mCaptionX;
-    private final int mCaptionY;
     private int mMarginMenuTop;
     private int mMarginMenuStart;
     private int mMenuHeight;
@@ -74,16 +72,13 @@
     private HandleMenuAnimator mHandleMenuAnimator;
 
 
-    HandleMenu(WindowDecoration parentDecor, int layoutResId, int captionX, int captionY,
-            View.OnClickListener onClickListener, View.OnTouchListener onTouchListener,
-            Bitmap appIcon, CharSequence appName, boolean shouldShowWindowingPill,
-            int captionHeight) {
+    HandleMenu(WindowDecoration parentDecor, int layoutResId, View.OnClickListener onClickListener,
+            View.OnTouchListener onTouchListener, Bitmap appIcon, CharSequence appName,
+            boolean shouldShowWindowingPill, int captionHeight) {
         mParentDecor = parentDecor;
         mContext = mParentDecor.mDecorWindowContext;
         mTaskInfo = mParentDecor.mTaskInfo;
         mLayoutResId = layoutResId;
-        mCaptionX = captionX;
-        mCaptionY = captionY;
         mOnClickListener = onClickListener;
         mOnTouchListener = onTouchListener;
         mAppIconBitmap = appIcon;
@@ -225,12 +220,12 @@
         if (mLayoutResId
                 == R.layout.desktop_mode_app_controls_window_decor) {
             // Align the handle menu to the left of the caption.
-            menuX = mCaptionX + mMarginMenuStart;
-            menuY = mCaptionY + mMarginMenuTop;
+            menuX = mMarginMenuStart;
+            menuY = mMarginMenuTop;
         } else {
             // Position the handle menu at the center of the caption.
-            menuX = mCaptionX + (captionWidth / 2) - (mMenuWidth / 2);
-            menuY = mCaptionY + mMarginMenuStart;
+            menuX = (captionWidth / 2) - (mMenuWidth / 2);
+            menuY = mMarginMenuStart;
         }
 
         // Handle Menu position setup.
@@ -346,8 +341,6 @@
         private View.OnClickListener mOnClickListener;
         private View.OnTouchListener mOnTouchListener;
         private int mLayoutId;
-        private int mCaptionX;
-        private int mCaptionY;
         private boolean mShowWindowingPill;
         private int mCaptionHeight;
 
@@ -381,12 +374,6 @@
             return this;
         }
 
-        Builder setCaptionPosition(int captionX, int captionY) {
-            mCaptionX = captionX;
-            mCaptionY = captionY;
-            return this;
-        }
-
         Builder setWindowingButtonsVisible(boolean windowingButtonsVisible) {
             mShowWindowingPill = windowingButtonsVisible;
             return this;
@@ -398,8 +385,8 @@
         }
 
         HandleMenu build() {
-            return new HandleMenu(mParent, mLayoutId, mCaptionX, mCaptionY, mOnClickListener,
-                    mOnTouchListener, mAppIcon, mName, mShowWindowingPill, mCaptionHeight);
+            return new HandleMenu(mParent, mLayoutId, mOnClickListener, mOnTouchListener,
+                    mAppIcon, mName, mShowWindowingPill, mCaptionHeight);
         }
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index b5373c6..6a9258c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -279,9 +279,12 @@
         }
 
         outResult.mCaptionHeight = loadDimensionPixelSize(resources, params.mCaptionHeightId);
-        final int captionWidth = taskBounds.width();
+        final int captionWidth = params.mCaptionWidthId != Resources.ID_NULL
+                ? loadDimensionPixelSize(resources, params.mCaptionWidthId) : taskBounds.width();
+        outResult.mCaptionX = (outResult.mWidth - captionWidth) / 2;
 
         startT.setWindowCrop(mCaptionContainerSurface, captionWidth, outResult.mCaptionHeight)
+                .setPosition(mCaptionContainerSurface, outResult.mCaptionX, 0 /* y */)
                 .setLayer(mCaptionContainerSurface, CAPTION_LAYER_Z_ORDER)
                 .show(mCaptionContainerSurface);
 
@@ -292,7 +295,7 @@
             mCaptionInsetsRect.set(taskBounds);
             if (mIsCaptionVisible) {
                 mCaptionInsetsRect.bottom =
-                        mCaptionInsetsRect.top + outResult.mCaptionHeight + params.mCaptionY;
+                        mCaptionInsetsRect.top + outResult.mCaptionHeight;
                 wct.addInsetsSource(mTaskInfo.token,
                         mOwner, 0 /* index */, WindowInsets.Type.captionBar(), mCaptionInsetsRect);
                 wct.addInsetsSource(mTaskInfo.token,
@@ -554,9 +557,6 @@
 
         int mCornerRadius;
 
-        int mCaptionX;
-        int mCaptionY;
-
         Configuration mWindowDecorConfig;
 
         boolean mApplyStartTransactionOnDraw;
@@ -570,9 +570,6 @@
 
             mCornerRadius = 0;
 
-            mCaptionX = 0;
-            mCaptionY = 0;
-
             mApplyStartTransactionOnDraw = false;
             mSetTaskPositionAndCrop = false;
             mWindowDecorConfig = null;
@@ -581,6 +578,7 @@
 
     static class RelayoutResult<T extends View & TaskFocusStateConsumer> {
         int mCaptionHeight;
+        int mCaptionX;
         int mWidth;
         int mHeight;
         T mRootView;
@@ -589,6 +587,7 @@
             mWidth = 0;
             mHeight = 0;
             mCaptionHeight = 0;
+            mCaptionX = 0;
             mRootView = null;
         }
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeFocusedWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeFocusedWindowDecorationViewHolder.kt
index 4930cb7..5f77022 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeFocusedWindowDecorationViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeFocusedWindowDecorationViewHolder.kt
@@ -5,6 +5,7 @@
 import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
 import android.content.res.ColorStateList
 import android.graphics.Color
+import android.graphics.PointF
 import android.view.View
 import android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
 import android.widget.ImageButton
@@ -35,9 +36,6 @@
     }
 
     override fun bindData(taskInfo: RunningTaskInfo) {
-        taskInfo.taskDescription?.statusBarColor?.let { captionColor ->
-            captionView.setBackgroundColor(captionColor)
-        }
         captionHandle.imageTintList = ColorStateList.valueOf(getCaptionHandleBarColor(taskInfo))
     }
 
@@ -49,6 +47,17 @@
         animateCaptionHandleAlpha(startValue = 0f, endValue = 1f)
     }
 
+    /**
+     * Returns true if input point is in the caption's view.
+     * @param inputPoint the input point relative to the task in full "focus" (i.e. fullscreen).
+     */
+    fun pointInCaption(inputPoint: PointF, captionX: Int): Boolean {
+        return inputPoint.x >= captionX &&
+                inputPoint.x <= captionX + captionView.width &&
+                inputPoint.y >= 0 &&
+                inputPoint.y <= captionView.height
+    }
+
     private fun getCaptionHandleBarColor(taskInfo: RunningTaskInfo): Int {
         return if (shouldUseLightCaptionColors(taskInfo)) {
             context.getColor(R.color.desktop_mode_caption_handle_bar_light)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeWindowDecorationViewHolder.kt
index 690b4e4..81bc34c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeWindowDecorationViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeWindowDecorationViewHolder.kt
@@ -17,9 +17,9 @@
    */
   abstract fun bindData(taskInfo: RunningTaskInfo)
 
-    /** Callback when the handle menu is opened. */
-    abstract fun onHandleMenuOpened()
+  /** Callback when the handle menu is opened. */
+  abstract fun onHandleMenuOpened()
 
-    /** Callback when the handle menu is closed. */
-    abstract fun onHandleMenuClosed()
+  /** Callback when the handle menu is closed. */
+  abstract fun onHandleMenuClosed()
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt
index 32f1259..143f7a7 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt
@@ -69,7 +69,7 @@
         setup {
             standardAppHelper.launchViaIntent(
                 wmHelper,
-                NetflixAppHelper.getNetflixWatchVideoIntent("70184207"),
+                NetflixAppHelper.getNetflixWatchVideoIntent("81605060"),
                 ComponentNameMatcher(NetflixAppHelper.PACKAGE_NAME, NetflixAppHelper.WATCH_ACTIVITY)
             )
             standardAppHelper.waitForVideoPlaying()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index 32a91461..7b53f70 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -772,15 +772,13 @@
 
         private WindowDecoration.AdditionalWindow addTestWindow() {
             final Resources resources = mDecorWindowContext.getResources();
-            int x = mRelayoutParams.mCaptionX;
-            int y = mRelayoutParams.mCaptionY;
             int width = loadDimensionPixelSize(resources, mCaptionMenuWidthId);
             int height = loadDimensionPixelSize(resources, mRelayoutParams.mCaptionHeightId);
             String name = "Test Window";
             WindowDecoration.AdditionalWindow additionalWindow =
                     addWindow(R.layout.desktop_mode_window_decor_handle_menu, name,
-                            mMockSurfaceControlAddWindowT, mMockSurfaceSyncGroup, x, y,
-                            width, height);
+                            mMockSurfaceControlAddWindowT, mMockSurfaceSyncGroup, 0 /* x */,
+                            0 /* y */, width, height);
             return additionalWindow;
         }
     }
diff --git a/location/java/android/location/flags/gnss.aconfig b/location/java/android/location/flags/gnss.aconfig
index 794a555..5f1279e 100644
--- a/location/java/android/location/flags/gnss.aconfig
+++ b/location/java/android/location/flags/gnss.aconfig
@@ -41,3 +41,10 @@
     description: "Flag for GNSS configuration from resource"
     bug: "317734846"
 }
+
+flag {
+    name: "replace_future_elapsed_realtime_jni"
+    namespace: "location"
+    description: "Flag for replacing future elapsedRealtime in JNI"
+    bug: "314328533"
+}
diff --git a/packages/CompanionDeviceManager/res/values-ar/strings.xml b/packages/CompanionDeviceManager/res/values-ar/strings.xml
index 5b893b0..6ddd5d3 100644
--- a/packages/CompanionDeviceManager/res/values-ar/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ar/strings.xml
@@ -56,28 +56,18 @@
     <string name="permission_nearby_devices" msgid="7530973297737123481">"الأجهزة المجاورة"</string>
     <string name="permission_media_routing_control" msgid="5498639511586715253">"تغيير جهاز إخراج الوسائط"</string>
     <string name="permission_storage" msgid="6831099350839392343">"الصور والوسائط"</string>
-    <!-- no translation found for permission_notifications (4099418516590632909) -->
-    <skip />
+    <string name="permission_notifications" msgid="4099418516590632909">"الإشعارات"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"التطبيقات"</string>
     <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"البثّ"</string>
-    <!-- no translation found for permission_phone_summary (8246321093970051702) -->
-    <skip />
-    <!-- no translation found for permission_call_logs_summary (7545243592757693321) -->
-    <skip />
-    <!-- no translation found for permission_sms_summary (8499509535410068616) -->
-    <skip />
-    <!-- no translation found for permission_contacts_summary (2840800622763086808) -->
-    <skip />
-    <!-- no translation found for permission_calendar_summary (8430353935747336165) -->
-    <skip />
-    <!-- no translation found for permission_microphone_summary (4862628553869973259) -->
-    <skip />
-    <!-- no translation found for permission_nearby_devices_summary (1306752848196464817) -->
-    <skip />
-    <!-- no translation found for permission_notification_listener_access_summary (7856071768185367749) -->
-    <skip />
-    <!-- no translation found for permission_notifications_summary (2272810466047367030) -->
-    <skip />
+    <string name="permission_phone_summary" msgid="8246321093970051702">"إجراء المكالمات الهاتفية وإدارتها"</string>
+    <string name="permission_call_logs_summary" msgid="7545243592757693321">"قراءة سجلّ المكالمات الهاتفية والكتابة إليه"</string>
+    <string name="permission_sms_summary" msgid="8499509535410068616">"إرسال الرسائل القصيرة وعرضها"</string>
+    <string name="permission_contacts_summary" msgid="2840800622763086808">"الوصول إلى جهات اتصالك"</string>
+    <string name="permission_calendar_summary" msgid="8430353935747336165">"الوصول إلى تقويمك"</string>
+    <string name="permission_microphone_summary" msgid="4862628553869973259">"تسجيل الصوت"</string>
+    <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"يمكن العثور على الموضع النسبي للأجهزة المجاورة والربط بها وتحديدها."</string>
+    <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"يمكن لهذا الملف الشخصي قراءة جميع الإشعارات، بما في ذلك المعلومات، مثل جهات الاتصال والرسائل والصور."</string>
+    <string name="permission_notifications_summary" msgid="2272810466047367030">"‏• قراءة كل الإشعارات بما فيها المعلومات، مثل جهات الاتصال والرسائل والصور&lt;br/&gt;• إرسال الإشعارات&lt;br/&gt;&lt;br/&gt;يمكنك إدارة الإذن الممنوح لهذا التطبيق بقراءة الإشعارات وإرسالها في أي وقت من خلال الإعدادات &gt; الإشعارات."</string>
     <string name="permission_app_streaming_summary" msgid="606923325679670624">"بث تطبيقات هاتفك"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
     <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"بثّ التطبيقات وميزات النظام الأخرى من هاتفك"</string>
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
index a78509d..c0d7149 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
@@ -53,6 +53,7 @@
     isNewActivity: Boolean,
 ) {
     val requestInfo: RequestInfo?
+    var isReqForAllOptions: Boolean = false
     private val providerEnabledList: List<ProviderData>
     private val providerDisabledList: List<DisabledProviderData>?
     val resultReceiver: ResultReceiver?
@@ -102,6 +103,11 @@
             ResultReceiver::class.java
         )
 
+        isReqForAllOptions = intent.getBooleanExtra(
+                Constants.EXTRA_REQ_FOR_ALL_OPTIONS,
+                /*defaultValue=*/ false
+        )
+
         val cancellationRequest = getCancelUiRequest(intent)
         val cancelUiRequestState = cancellationRequest?.let {
             CancelUiRequestState(getAppLabel(context.getPackageManager(), it.appPackageName))
@@ -141,7 +147,8 @@
                 )
             }
             RequestInfo.TYPE_GET -> {
-                val getCredentialInitialUiState = getCredentialInitialUiState(originName)!!
+                val getCredentialInitialUiState = getCredentialInitialUiState(originName,
+                        isReqForAllOptions)!!
                 val autoSelectEntry =
                     findAutoSelectEntry(getCredentialInitialUiState.providerDisplayInfo)
                 UiState(
@@ -216,14 +223,18 @@
     }
 
     // IMPORTANT: new invocation should be mindful that this method can throw.
-    private fun getCredentialInitialUiState(originName: String?): GetCredentialUiState? {
+    private fun getCredentialInitialUiState(
+            originName: String?,
+            isReqForAllOptions: Boolean
+    ): GetCredentialUiState? {
         val providerEnabledList = GetFlowUtils.toProviderList(
             providerEnabledList as List<GetCredentialProviderData>, context
         )
         val requestDisplayInfo = GetFlowUtils.toRequestDisplayInfo(requestInfo, context, originName)
         return GetCredentialUiState(
-            providerEnabledList,
-            requestDisplayInfo ?: return null,
+                isReqForAllOptions,
+                providerEnabledList,
+                requestDisplayInfo ?: return null
         )
     }
 
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
index 46bebc4..a291f59 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
@@ -26,10 +26,12 @@
 import com.android.internal.util.Preconditions
 
 data class GetCredentialUiState(
+        val isRequestForAllOptions: Boolean,
     val providerInfoList: List<ProviderInfo>,
     val requestDisplayInfo: RequestDisplayInfo,
     val providerDisplayInfo: ProviderDisplayInfo = toProviderDisplayInfo(providerInfoList),
-    val currentScreenState: GetScreenState = toGetScreenState(providerDisplayInfo),
+    val currentScreenState: GetScreenState = toGetScreenState(
+            providerDisplayInfo, isRequestForAllOptions),
     val activeEntry: EntryInfo? = toActiveEntry(providerDisplayInfo),
     val isNoAccount: Boolean = false,
 )
@@ -184,7 +186,8 @@
 }
 
 private fun toGetScreenState(
-    providerDisplayInfo: ProviderDisplayInfo
+    providerDisplayInfo: ProviderDisplayInfo,
+    isRequestForAllOptions: Boolean
 ): GetScreenState {
     return if (providerDisplayInfo.sortedUserNameToCredentialEntryList.isEmpty() &&
         providerDisplayInfo.remoteEntry == null &&
@@ -194,6 +197,8 @@
         providerDisplayInfo.authenticationEntryList.isEmpty() &&
         providerDisplayInfo.remoteEntry != null)
         GetScreenState.REMOTE_ONLY
+    else if (isRequestForAllOptions)
+        GetScreenState.ALL_SIGN_IN_OPTIONS
     else GetScreenState.PRIMARY_SELECTION
 }
 
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
index 2a7e9e1..59e6142 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
@@ -21,11 +21,14 @@
 import androidx.lifecycle.viewModelScope
 import com.android.credentialmanager.model.Request
 import com.android.credentialmanager.client.CredentialManagerClient
+import com.android.credentialmanager.model.get.ActionEntryInfo
+import com.android.credentialmanager.model.get.CredentialEntryInfo
 import com.android.credentialmanager.ui.mappers.toGet
 import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.stateIn
 import javax.inject.Inject
 
@@ -33,15 +36,15 @@
 class CredentialSelectorViewModel @Inject constructor(
     private val credentialManagerClient: CredentialManagerClient,
 ) : ViewModel() {
-
+    private val isPrimaryScreen = MutableStateFlow(false)
     val uiState: StateFlow<CredentialSelectorUiState> = credentialManagerClient.requests
-        .map { request ->
+        .combine(isPrimaryScreen) { request, isPrimary ->
             when (request) {
                 null -> CredentialSelectorUiState.Idle
                 is Request.Cancel -> CredentialSelectorUiState.Cancel(request.appName)
                 is Request.Close -> CredentialSelectorUiState.Close
                 is Request.Create -> CredentialSelectorUiState.Create
-                is Request.Get -> request.toGet()
+                is Request.Get -> request.toGet(isPrimary)
             }
         }
         .stateIn(
@@ -57,9 +60,18 @@
 
 sealed class CredentialSelectorUiState {
     data object Idle : CredentialSelectorUiState()
-    sealed class Get : CredentialSelectorUiState() {
-        data object SingleProviderSinglePasskey : Get()
-        data object SingleProviderSinglePassword : Get()
+    sealed class Get() : CredentialSelectorUiState() {
+        data class SingleEntry(val entry: CredentialEntryInfo) : Get()
+        data class SingleEntryPerAccount(val sortedEntries: List<CredentialEntryInfo>) : Get()
+        data class MultipleEntry(
+            val accounts: List<PerUserNameEntries>,
+            val actionEntryList: List<ActionEntryInfo>,
+        ) : Get() {
+            data class PerUserNameEntries(
+                val userName: String,
+                val sortedCredentialEntryList: List<CredentialEntryInfo>,
+            )
+        }
 
         // TODO: b/301206470 add the remaining states
     }
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
index 7e0ea30..790f5b1 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
@@ -26,6 +26,7 @@
 import androidx.wear.compose.navigation.rememberSwipeDismissableNavController
 import androidx.wear.compose.navigation.rememberSwipeDismissableNavHostState
 import com.android.credentialmanager.CredentialSelectorUiState
+import com.android.credentialmanager.CredentialSelectorUiState.Get.SingleEntry
 import com.android.credentialmanager.CredentialSelectorViewModel
 import com.android.credentialmanager.ui.screens.LoadingScreen
 import com.android.credentialmanager.ui.screens.single.password.SinglePasswordScreen
@@ -57,6 +58,7 @@
 
         scrollable(Screen.SinglePasswordScreen.route) {
             SinglePasswordScreen(
+                state = viewModel.uiState.value as SingleEntry,
                 columnState = it.columnState,
                 onCloseApp = onCloseApp,
             )
@@ -100,7 +102,7 @@
     onCloseApp: () -> Unit,
 ) {
     when (state) {
-        is CredentialSelectorUiState.Get.SingleProviderSinglePassword -> {
+        is SingleEntry -> {
             navController.navigateToSinglePasswordScreen()
         }
 
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
index 14b992a..44a838d 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
@@ -18,18 +18,45 @@
 
 import com.android.credentialmanager.model.Request
 import com.android.credentialmanager.CredentialSelectorUiState
+import com.android.credentialmanager.CredentialSelectorUiState.Get.MultipleEntry.PerUserNameEntries
+import com.android.credentialmanager.model.CredentialType
+import com.android.credentialmanager.model.get.CredentialEntryInfo
 
-fun Request.Get.toGet(): CredentialSelectorUiState.Get {
+fun Request.Get.toGet(isPrimary: Boolean): CredentialSelectorUiState.Get {
     // TODO: b/301206470 returning a hard coded state for MVP
-    if (true) return CredentialSelectorUiState.Get.SingleProviderSinglePassword
-
-    return if (providerInfos.size == 1) {
-        if (providerInfos.first().credentialEntryList.size == 1) {
-            CredentialSelectorUiState.Get.SingleProviderSinglePassword
+    if (true) return CredentialSelectorUiState.Get.SingleEntry(
+        providerInfos
+            .flatMap { it.credentialEntryList }
+            .first { it.credentialType == CredentialType.PASSWORD }
+    )
+    val accounts = providerInfos
+        .flatMap { it.credentialEntryList }
+        .groupBy { it.userName}
+        .entries
+        .toList()
+    return if (isPrimary) {
+        if (accounts.size == 1) {
+            CredentialSelectorUiState.Get.SingleEntry(
+                accounts[0].value.minWith(comparator)
+            )
         } else {
-            TODO() // b/301206470 - Implement other get flows
+            CredentialSelectorUiState.Get.SingleEntryPerAccount(
+                accounts.map { it.value.minWith(comparator) }.sortedWith(comparator)
+            )
         }
     } else {
-        TODO() // b/301206470 - Implement other get flows
+        CredentialSelectorUiState.Get.MultipleEntry(
+            accounts = accounts.map { PerUserNameEntries(
+                it.key,
+                it.value.sortedWith(comparator)
+            )
+            },
+            actionEntryList = providerInfos.flatMap { it.actionEntryList },
+        )
     }
 }
+val comparator = compareBy<CredentialEntryInfo> { entryInfo ->
+    // Passkey type always go first
+    entryInfo.credentialType.let{ if (it == CredentialType.PASSKEY) 0 else 1 }
+}
+    .thenByDescending{ it.lastUsedTimeMillis }
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
index b64f581..9f971ae 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
@@ -29,6 +29,7 @@
 import androidx.compose.ui.unit.dp
 import androidx.hilt.navigation.compose.hiltViewModel
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.credentialmanager.CredentialSelectorUiState.Get.SingleEntry
 import com.android.credentialmanager.R
 import com.android.credentialmanager.TAG
 import com.android.credentialmanager.activity.StartBalIntentSenderForResultContract
@@ -44,12 +45,13 @@
 
 @Composable
 fun SinglePasswordScreen(
+    state: SingleEntry,
     columnState: ScalingLazyColumnState,
     onCloseApp: () -> Unit,
     modifier: Modifier = Modifier,
     viewModel: SinglePasswordScreenViewModel = hiltViewModel(),
 ) {
-    viewModel.initialize()
+    viewModel.initialize(state.entry)
 
     val uiState by viewModel.uiState.collectAsStateWithLifecycle()
 
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt
index 26bee1f..4f9fc46 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt
@@ -19,12 +19,9 @@
 import android.content.Intent
 import android.credentials.ui.ProviderPendingIntentResponse
 import android.credentials.ui.UserSelectionDialogResult
-import android.util.Log
 import androidx.activity.result.IntentSenderRequest
 import androidx.annotation.MainThread
 import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
-import com.android.credentialmanager.TAG
 import com.android.credentialmanager.ktx.getIntentSenderRequest
 import com.android.credentialmanager.model.Request
 import com.android.credentialmanager.client.CredentialManagerClient
@@ -33,7 +30,6 @@
 import dagger.hilt.android.lifecycle.HiltViewModel
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.launch
 import javax.inject.Inject
 
 @HiltViewModel
@@ -51,32 +47,14 @@
     val uiState: StateFlow<SinglePasswordScreenUiState> = _uiState
 
     @MainThread
-    fun initialize() {
+    fun initialize(entryInfo: CredentialEntryInfo) {
         if (initializeCalled) return
         initializeCalled = true
-
-        viewModelScope.launch {
-            val request = credentialManagerClient.requests.value
-            Log.d(TAG, "request: $request, client instance: $credentialManagerClient")
-
-            if (request !is Request.Get) {
-                _uiState.value = SinglePasswordScreenUiState.Error
-            } else {
-                requestGet = request
-
-                if (requestGet.providerInfos.all { it.credentialEntryList.isEmpty() }) {
-                    Log.d(TAG, "Empty passwordEntries")
-                    _uiState.value = SinglePasswordScreenUiState.Error
-                } else {
-                    entryInfo = requestGet.providerInfos.first().credentialEntryList.first()
-                    _uiState.value = SinglePasswordScreenUiState.Loaded(
-                        PasswordUiModel(
-                            email = entryInfo.userName,
-                        )
-                    )
-                }
-            }
-        }
+        _uiState.value = SinglePasswordScreenUiState.Loaded(
+            PasswordUiModel(
+                email = entryInfo.userName,
+            )
+        )
     }
 
     fun onCancelClick() {
diff --git a/packages/InputDevices/res/values-uk/strings.xml b/packages/InputDevices/res/values-uk/strings.xml
index 5368f2c..71b3496 100644
--- a/packages/InputDevices/res/values-uk/strings.xml
+++ b/packages/InputDevices/res/values-uk/strings.xml
@@ -3,49 +3,49 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="8016145283189546017">"Пристрої вводу"</string>
     <string name="keyboard_layouts_label" msgid="6688773268302087545">"Клавіатура Android"</string>
-    <string name="keyboard_layout_english_uk_label" msgid="6664258463319999632">"англійська (Велика Британія)"</string>
-    <string name="keyboard_layout_english_us_label" msgid="8994890249649106291">"англійська (США)"</string>
-    <string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"англійська (США), міжнародна"</string>
-    <string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"англійська (США), розкладка Colemak"</string>
-    <string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"англійська (США), розкладка Дворака"</string>
-    <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"англійська (США), розкладка Workman"</string>
-    <string name="keyboard_layout_german_label" msgid="8451565865467909999">"німецька"</string>
-    <string name="keyboard_layout_french_label" msgid="813450119589383723">"французька"</string>
-    <string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"французька (Канада)"</string>
-    <string name="keyboard_layout_russian_label" msgid="8724879775815042968">"російська"</string>
-    <string name="keyboard_layout_russian_mac_label" msgid="3795866869038264796">"російська, розкладка Mac"</string>
-    <string name="keyboard_layout_spanish_label" msgid="7091555148131908240">"іспанська"</string>
-    <string name="keyboard_layout_swiss_french_label" msgid="4659191025396371684">"французька (Швейцарія)"</string>
-    <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"німецька (Швейцарія)"</string>
-    <string name="keyboard_layout_belgian" msgid="2011984572838651558">"бельгійська"</string>
-    <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"болгарська"</string>
+    <string name="keyboard_layout_english_uk_label" msgid="6664258463319999632">"Англійська (Велика Британія)"</string>
+    <string name="keyboard_layout_english_us_label" msgid="8994890249649106291">"Англійська (США)"</string>
+    <string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Англійська (США), міжнародна"</string>
+    <string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Англійська (США), розкладка Colemak"</string>
+    <string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Англійська (США), розкладка Дворака"</string>
+    <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Англійська (США), розкладка Workman"</string>
+    <string name="keyboard_layout_german_label" msgid="8451565865467909999">"Німецька"</string>
+    <string name="keyboard_layout_french_label" msgid="813450119589383723">"Французька"</string>
+    <string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Французька (Канада)"</string>
+    <string name="keyboard_layout_russian_label" msgid="8724879775815042968">"Російська"</string>
+    <string name="keyboard_layout_russian_mac_label" msgid="3795866869038264796">"Російська, розкладка Mac"</string>
+    <string name="keyboard_layout_spanish_label" msgid="7091555148131908240">"Іспанська"</string>
+    <string name="keyboard_layout_swiss_french_label" msgid="4659191025396371684">"Французька (Швейцарія)"</string>
+    <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Німецька (Швейцарія)"</string>
+    <string name="keyboard_layout_belgian" msgid="2011984572838651558">"Бельгійська"</string>
+    <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Болгарська"</string>
     <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Болгарська (фонетична)"</string>
-    <string name="keyboard_layout_italian" msgid="6497079660449781213">"італійська"</string>
-    <string name="keyboard_layout_danish" msgid="8036432066627127851">"данська"</string>
-    <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"норвезька"</string>
-    <string name="keyboard_layout_swedish" msgid="732959109088479351">"шведська"</string>
-    <string name="keyboard_layout_finnish" msgid="5585659438924315466">"фінська"</string>
-    <string name="keyboard_layout_croatian" msgid="4172229471079281138">"хорватська"</string>
-    <string name="keyboard_layout_czech" msgid="1349256901452975343">"чеська"</string>
+    <string name="keyboard_layout_italian" msgid="6497079660449781213">"Італійська"</string>
+    <string name="keyboard_layout_danish" msgid="8036432066627127851">"Данська"</string>
+    <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Норвезька"</string>
+    <string name="keyboard_layout_swedish" msgid="732959109088479351">"Шведська"</string>
+    <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Фінська"</string>
+    <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Хорватська"</string>
+    <string name="keyboard_layout_czech" msgid="1349256901452975343">"Чеська"</string>
     <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Чеська (QWERTY)"</string>
-    <string name="keyboard_layout_estonian" msgid="8775830985185665274">"естонська"</string>
-    <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"угорська"</string>
-    <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"ісландська"</string>
-    <string name="keyboard_layout_brazilian" msgid="5117896443147781939">"бразильська"</string>
-    <string name="keyboard_layout_portuguese" msgid="2888198587329660305">"португальська"</string>
-    <string name="keyboard_layout_slovak" msgid="2469379934672837296">"словацька"</string>
-    <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"словенська"</string>
-    <string name="keyboard_layout_turkish" msgid="7736163250907964898">"турецька"</string>
+    <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Естонська"</string>
+    <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Угорська"</string>
+    <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Ісландська"</string>
+    <string name="keyboard_layout_brazilian" msgid="5117896443147781939">"Бразильська"</string>
+    <string name="keyboard_layout_portuguese" msgid="2888198587329660305">"Португальська"</string>
+    <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Словацька"</string>
+    <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Словенська"</string>
+    <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Турецька"</string>
     <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Турецька-F"</string>
-    <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"українська"</string>
-    <string name="keyboard_layout_arabic" msgid="5671970465174968712">"арабська"</string>
+    <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Українська"</string>
+    <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Арабська"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"Грецька"</string>
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"Іврит"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"Литовська"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"Іспанська (латиниця)"</string>
     <string name="keyboard_layout_latvian" msgid="4405417142306250595">"Латвійська"</string>
     <string name="keyboard_layout_persian" msgid="3920643161015888527">"Перська"</string>
-    <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"азербайджанська"</string>
+    <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Азербайджанська"</string>
     <string name="keyboard_layout_polish" msgid="1121588624094925325">"Польська"</string>
     <string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Білоруська"</string>
     <string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Монгольська"</string>
diff --git a/packages/PackageInstaller/res/values-ar/strings.xml b/packages/PackageInstaller/res/values-ar/strings.xml
index e4da5b7..224c6dc 100644
--- a/packages/PackageInstaller/res/values-ar/strings.xml
+++ b/packages/PackageInstaller/res/values-ar/strings.xml
@@ -44,8 +44,7 @@
     <string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"يتعذر على هذا المستخدم تثبيت التطبيقات غير المعروفة"</string>
     <string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"غير مسموح لهذا المستخدم بتثبيت التطبيقات"</string>
     <string name="ok" msgid="7871959885003339302">"حسنًا"</string>
-    <!-- no translation found for archive (4447791830199354721) -->
-    <skip />
+    <string name="archive" msgid="4447791830199354721">"أرشفة"</string>
     <string name="update_anyway" msgid="8792432341346261969">"التحديث على أي حال"</string>
     <string name="manage_applications" msgid="5400164782453975580">"إدارة التطبيقات"</string>
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"نفدت مساحة التخزين"</string>
@@ -60,16 +59,11 @@
     <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>
-    <!-- no translation found for archive_application_text (8482325710714386348) -->
-    <skip />
-    <!-- no translation found for archive_application_text_all_users (3151229641681672580) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_work_profile (1450487362134779752) -->
-    <skip />
-    <!-- no translation found for archive_application_text_user (2586558895535581451) -->
-    <skip />
-    <!-- no translation found for archive_application_text_current_user_private_profile (1958423158655599132) -->
-    <skip />
+    <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>
+    <string name="archive_application_text_user" msgid="2586558895535581451">"هل تريد أرشفة هذا التطبيق لـ \"<xliff:g id="USERNAME">%1$s</xliff:g>\"؟ سيتم حفظ بياناتك الشخصية."</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"هل تريد أرشفة هذا التطبيق المحفوظ في المساحة الخاصّة؟ سيتم حفظ بياناتك الشخصية."</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"هل تريد إزالة هذا التطبيق "<b>"لكل"</b>" المستخدمين؟ ستتم إزالة التطبيق وبياناته من "<b>"كل"</b>" المستخدمين على هذا الجهاز."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"هل تريد إزالة هذا التطبيق للمستخدم <xliff:g id="USERNAME">%1$s</xliff:g>؟"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"هل تريد إزالة تثبيت هذا التطبيق من ملفك الشخصي للعمل؟"</string>
@@ -108,8 +102,7 @@
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"يعتبر الجهاز اللوحي والبيانات الشخصية أكثر عرضة لهجوم التطبيقات غير المعروفة. من خلال تثبيت هذا التطبيق، توافق على تحمل مسؤولية أي ضرر يحدث للجهاز اللوحي أو فقدان البيانات الذي قد ينتج عن استخدامه."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"يعتبر جهاز التلفزيون والبيانات الشخصية أكثر عرضة لهجوم التطبيقات غير المعروفة. من خلال تثبيت هذا التطبيق، توافق على تحمل مسؤولية أي ضرر يحدث لجهاز التلفزيون أو فقدان البيانات الذي قد ينتج عن استخدامه."</string>
     <string name="cloned_app_label" msgid="7503612829833756160">"نسخة طبق الأصل من \"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>\""</string>
-    <!-- no translation found for archiving_app_label (1127085259724124725) -->
-    <skip />
+    <string name="archiving_app_label" msgid="1127085259724124725">"هل تريد أرشفة <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>؟"</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"متابعة"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"الإعدادات"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"‏تثبيت / إلغاء تثبيت تطبيقات Android Wear"</string>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallConfirmationFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallConfirmationFragment.java
index 5ca02ea..dbe32cc 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallConfirmationFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallConfirmationFragment.java
@@ -60,20 +60,29 @@
     public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
         View dialogView = getLayoutInflater().inflate(R.layout.install_content_view, null);
 
+        int positiveBtnTextRes;
+        if (mDialogData.isAppUpdating()) {
+            if (mDialogData.getDialogMessage() != null) {
+                positiveBtnTextRes = R.string.update_anyway;
+            } else {
+                positiveBtnTextRes = R.string.update;
+            }
+        } else {
+            positiveBtnTextRes = R.string.install;
+        }
+
         mDialog = new AlertDialog.Builder(requireContext())
             .setIcon(mDialogData.getAppIcon())
             .setTitle(mDialogData.getAppLabel())
             .setView(dialogView)
-            .setPositiveButton(mDialogData.isAppUpdating() ? R.string.update : R.string.install,
+            .setPositiveButton(positiveBtnTextRes,
                 (dialogInt, which) -> mInstallActionListener.onPositiveResponse(
                     InstallUserActionRequired.USER_ACTION_REASON_INSTALL_CONFIRMATION))
             .setNegativeButton(R.string.cancel,
                 (dialogInt, which) -> mInstallActionListener.onNegativeResponse(
                     mDialogData.getStageCode()))
-
             .create();
 
-        // TODO: Dynamically change positive button text to update anyway
         TextView viewToEnable;
         if (mDialogData.isAppUpdating()) {
             viewToEnable = dialogView.requireViewById(R.id.install_confirm_question_update);
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 1cdb69c..a94c313 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -140,8 +140,8 @@
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Tumia kwa kuingiza"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Tumia kwa visaidizi vya kusikia"</string>
     <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Tumia kwa ajili ya LE_AUDIO"</string>
-    <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Oanisha"</string>
-    <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"OANISHA"</string>
+    <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Unganisha"</string>
+    <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"UNGANISHA"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Ghairi"</string>
     <string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"Kuoanisha hutoa ruhusa ya kufikiwa kwa unaowasiliana nao na rekodi ya simu zilizopigwa unapounganishwa."</string>
     <string name="bluetooth_pairing_error_message" msgid="6626399020672335565">"Haikuwezakulinganisha na <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
@@ -237,10 +237,10 @@
     <string name="adb_wireless_error" msgid="721958772149779856">"Hitilafu"</string>
     <string name="adb_wireless_settings" msgid="2295017847215680229">"Utatuzi usiotumia waya"</string>
     <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"Ili kuangalia na kutumia vifaa vinavyopatikana, washa utatuzi usiotumia waya"</string>
-    <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"Oanisha kifaa ukitumia msimbo wa QR"</string>
-    <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"Oanisha vifaa vipya ukitumia kichanganuzi cha Msimbo wa QR"</string>
-    <string name="adb_pair_method_code_title" msgid="1122590300445142904">"Oanisha kifaa ukitumia msimbo wa kuoanisha"</string>
-    <string name="adb_pair_method_code_summary" msgid="6370414511333685185">"Oanisha vifaa vipya ukitumia msimbo wa tarakimu sita"</string>
+    <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"Unganisha kifaa ukitumia msimbo wa QR"</string>
+    <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"Unganisha vifaa vipya ukitumia kichanganuzi cha Msimbo wa QR"</string>
+    <string name="adb_pair_method_code_title" msgid="1122590300445142904">"Unganisha kifaa ukitumia msimbo wa kuunganisha"</string>
+    <string name="adb_pair_method_code_summary" msgid="6370414511333685185">"Unganisha vifaa vipya ukitumia msimbo wa tarakimu sita"</string>
     <string name="adb_paired_devices_title" msgid="5268997341526217362">"Vifaa vilivyooanishwa"</string>
     <string name="adb_wireless_device_connected_summary" msgid="3039660790249148713">"Vilivyounganishwa kwa sasa"</string>
     <string name="adb_wireless_device_details_title" msgid="7129369670526565786">"Maelezo ya kifaa"</string>
@@ -248,16 +248,16 @@
     <string name="adb_device_fingerprint_title_format" msgid="291504822917843701">"Alama bainifu ya kifaa: <xliff:g id="FINGERPRINT_PARAM">%1$s</xliff:g>"</string>
     <string name="adb_wireless_connection_failed_title" msgid="664211177427438438">"Imeshindwa kuunganisha"</string>
     <string name="adb_wireless_connection_failed_message" msgid="9213896700171602073">"Hakikisha kuwa <xliff:g id="DEVICE_NAME">%1$s</xliff:g> kimeunganishwa kwenye mtandao sahihi"</string>
-    <string name="adb_pairing_device_dialog_title" msgid="7141739231018530210">"Oanisha na kifaa"</string>
+    <string name="adb_pairing_device_dialog_title" msgid="7141739231018530210">"Unganisha na kifaa"</string>
     <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"Msimbo wa kuoanisha wa Wi-Fi"</string>
     <string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"Imeshindwa kuoanisha"</string>
     <string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"Hakikisha kuwa kifaa kimeunganishwa kwenye mtandao mmoja."</string>
-    <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Oanisha kifaa kupitia Wi-Fi kwa kuchanganua msimbo wa QR"</string>
+    <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Unganisha kifaa kupitia Wi-Fi kwa kuchanganua msimbo wa QR"</string>
     <string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"Inaoanisha kifaa…"</string>
     <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Imeshindwa kuoanisha kifaa. Huenda msimbo wa QR haukuwa sahihi au kifaa hakijaunganishwa kwenye mtandao mmoja."</string>
     <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"Anwani ya IP na Mlango"</string>
     <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"Changanua msimbo wa QR"</string>
-    <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Oanisha kifaa kupitia Wi-Fi kwa kuchanganua msimbo wa QR"</string>
+    <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Unganisha kifaa kupitia Wi-Fi kwa kuchanganua msimbo wa QR"</string>
     <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Tafadhali unganisha kwenye mtandao wa Wi-Fi"</string>
     <string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, tatua, dev"</string>
     <string name="bugreport_in_power" msgid="8664089072534638709">"Njia ya mkato ya kuripoti hitilafu"</string>
diff --git a/packages/SystemUI/aconfig/Android.bp b/packages/SystemUI/aconfig/Android.bp
index 7f16ca5..03f9d74 100644
--- a/packages/SystemUI/aconfig/Android.bp
+++ b/packages/SystemUI/aconfig/Android.bp
@@ -25,6 +25,7 @@
         "//frameworks/base/packages/SystemUI:__subpackages__",
         "//frameworks/libs/systemui/tracinglib:__subpackages__",
         "//platform_testing:__subpackages__",
+        "//cts:__subpackages__",
     ],
 }
 
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/ui/platform/DensityAwareComposeView.kt b/packages/SystemUI/compose/core/src/com/android/compose/ui/platform/DensityAwareComposeView.kt
new file mode 100644
index 0000000..dff8753
--- /dev/null
+++ b/packages/SystemUI/compose/core/src/com/android/compose/ui/platform/DensityAwareComposeView.kt
@@ -0,0 +1,96 @@
+/*
+ * 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.compose.ui.platform
+
+import android.content.Context
+import android.content.res.Configuration
+import android.util.AttributeSet
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.ui.platform.AbstractComposeView
+
+/**
+ * A ComposeView that recreates its composition if the display size or font scale was changed.
+ *
+ * TODO(b/317317814): Remove this workaround.
+ */
+class DensityAwareComposeView(context: Context) : OpenComposeView(context) {
+    private var lastDensityDpi: Int = -1
+    private var lastFontScale: Float = -1f
+
+    override fun onAttachedToWindow() {
+        super.onAttachedToWindow()
+
+        val configuration = context.resources.configuration
+        lastDensityDpi = configuration.densityDpi
+        lastFontScale = configuration.fontScale
+    }
+
+    override fun dispatchConfigurationChanged(newConfig: Configuration) {
+        super.dispatchConfigurationChanged(newConfig)
+
+        // If the density or font scale changed, we dispose then recreate the composition. Note that
+        // we do this here after dispatching the new configuration to children (instead of doing
+        // this in onConfigurationChanged()) because the new configuration should first be
+        // dispatched to the AndroidComposeView that holds the current density before we recreate
+        // the composition.
+        val densityDpi = newConfig.densityDpi
+        val fontScale = newConfig.fontScale
+        if (densityDpi != lastDensityDpi || fontScale != lastFontScale) {
+            lastDensityDpi = densityDpi
+            lastFontScale = fontScale
+
+            disposeComposition()
+            if (isAttachedToWindow) {
+                createComposition()
+            }
+        }
+    }
+}
+
+/** A fork of [androidx.compose.ui.platform.ComposeView] that is open and can be subclassed. */
+open class OpenComposeView
+internal constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) :
+    AbstractComposeView(context, attrs, defStyleAttr) {
+
+    private val content = mutableStateOf<(@Composable () -> Unit)?>(null)
+
+    @Suppress("RedundantVisibilityModifier")
+    protected override var shouldCreateCompositionOnAttachedToWindow: Boolean = false
+
+    @Composable
+    override fun Content() {
+        content.value?.invoke()
+    }
+
+    override fun getAccessibilityClassName(): CharSequence {
+        return javaClass.name
+    }
+
+    /**
+     * Set the Jetpack Compose UI content for this view. Initial composition will occur when the
+     * view becomes attached to a window or when [createComposition] is called, whichever comes
+     * first.
+     */
+    fun setContent(content: @Composable () -> Unit) {
+        shouldCreateCompositionOnAttachedToWindow = true
+        this.content.value = content
+        if (isAttachedToWindow) {
+            createComposition()
+        }
+    }
+}
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt
index 5055ee1..d31547b 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt
@@ -27,6 +27,7 @@
 import androidx.compose.ui.unit.dp
 import androidx.lifecycle.LifecycleOwner
 import com.android.compose.theme.PlatformTheme
+import com.android.compose.ui.platform.DensityAwareComposeView
 import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation
 import com.android.systemui.common.ui.compose.windowinsets.DisplayCutout
 import com.android.systemui.common.ui.compose.windowinsets.DisplayCutoutProvider
@@ -84,7 +85,7 @@
         viewModel: FooterActionsViewModel,
         qsVisibilityLifecycleOwner: LifecycleOwner,
     ): View {
-        return ComposeView(context).apply {
+        return DensityAwareComposeView(context).apply {
             setContent { PlatformTheme { FooterActions(viewModel, qsVisibilityLifecycleOwner) } }
         }
     }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
index 67a6820..ff53ff2 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
@@ -37,9 +37,6 @@
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
 
-/** Set this to `true` to use the LockscreenContent replacement of KeyguardRootView. */
-private val UseLockscreenContent = false
-
 /** The lock screen scene shows when the device is locked. */
 @SysUISingleton
 class LockscreenScene
@@ -48,7 +45,6 @@
     @Application private val applicationScope: CoroutineScope,
     private val viewModel: LockscreenSceneViewModel,
     private val lockscreenContent: Lazy<LockscreenContent>,
-    private val viewBasedLockscreenContent: Lazy<ViewBasedLockscreenContent>,
 ) : ComposableScene {
     override val key = SceneKey.Lockscreen
 
@@ -73,7 +69,6 @@
     ) {
         LockscreenScene(
             lockscreenContent = lockscreenContent,
-            viewBasedLockscreenContent = viewBasedLockscreenContent,
             modifier = modifier,
         )
     }
@@ -93,22 +88,13 @@
 }
 
 @Composable
-private fun SceneScope.LockscreenScene(
+private fun LockscreenScene(
     lockscreenContent: Lazy<LockscreenContent>,
-    viewBasedLockscreenContent: Lazy<ViewBasedLockscreenContent>,
     modifier: Modifier = Modifier,
 ) {
-    if (UseLockscreenContent) {
-        lockscreenContent
-            .get()
-            .Content(
-                modifier = modifier.fillMaxSize(),
-            )
-    } else {
-        with(viewBasedLockscreenContent.get()) {
-            Content(
-                modifier = modifier.fillMaxSize(),
-            )
-        }
-    }
+    lockscreenContent
+        .get()
+        .Content(
+            modifier = modifier.fillMaxSize(),
+        )
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/ViewBasedLockscreenContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/ViewBasedLockscreenContent.kt
deleted file mode 100644
index 8119d2a..0000000
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/ViewBasedLockscreenContent.kt
+++ /dev/null
@@ -1,113 +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.keyguard.ui.composable
-
-import android.graphics.Rect
-import android.view.View
-import android.view.ViewGroup
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.getValue
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.toComposeRect
-import androidx.compose.ui.layout.Layout
-import androidx.compose.ui.layout.onPlaced
-import androidx.compose.ui.viewinterop.AndroidView
-import androidx.core.view.isVisible
-import com.android.compose.animation.scene.SceneScope
-import com.android.systemui.keyguard.qualifiers.KeyguardRootView
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
-import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel
-import com.android.systemui.notifications.ui.composable.NotificationStack
-import com.android.systemui.res.R
-import javax.inject.Inject
-
-/**
- * Renders the content of the lockscreen.
- *
- * This is different from [LockscreenContent] (which is pure compose) and uses a view-based
- * implementation of the lockscreen scene content that relies on [KeyguardRootView].
- *
- * TODO(b/316211368): remove this once [LockscreenContent] is feature complete.
- */
-class ViewBasedLockscreenContent
-@Inject
-constructor(
-    private val lockscreenSceneViewModel: LockscreenSceneViewModel,
-    @KeyguardRootView private val viewProvider: () -> @JvmSuppressWildcards View,
-    private val keyguardRootViewModel: KeyguardRootViewModel,
-) {
-    @Composable
-    fun SceneScope.Content(
-        modifier: Modifier = Modifier,
-    ) {
-        fun findSettingsMenu(): View {
-            return viewProvider().requireViewById(R.id.keyguard_settings_button)
-        }
-
-        LockscreenLongPress(
-            viewModel = lockscreenSceneViewModel.longPress,
-            modifier = modifier,
-        ) { onSettingsMenuPlaced ->
-            AndroidView(
-                factory = { _ ->
-                    val keyguardRootView = viewProvider()
-                    // Remove the KeyguardRootView from any parent it might already have in legacy
-                    // code just in case (a view can't have two parents).
-                    (keyguardRootView.parent as? ViewGroup)?.removeView(keyguardRootView)
-                    keyguardRootView
-                },
-                modifier = Modifier.fillMaxSize(),
-            )
-
-            val notificationStackPosition by
-                keyguardRootViewModel.notificationBounds.collectAsState()
-
-            Layout(
-                modifier =
-                    Modifier.fillMaxSize().onPlaced {
-                        val settingsMenuView = findSettingsMenu()
-                        onSettingsMenuPlaced(
-                            if (settingsMenuView.isVisible) {
-                                val bounds = Rect()
-                                settingsMenuView.getHitRect(bounds)
-                                bounds.toComposeRect()
-                            } else {
-                                null
-                            }
-                        )
-                    },
-                content = {
-                    NotificationStack(
-                        viewModel = lockscreenSceneViewModel.notifications,
-                        isScrimVisible = false,
-                    )
-                }
-            ) { measurables, constraints ->
-                check(measurables.size == 1)
-                val height = notificationStackPosition.height.toInt()
-                val childConstraints = constraints.copy(minHeight = height, maxHeight = height)
-                val placeable = measurables[0].measure(childConstraints)
-                layout(constraints.maxWidth, constraints.maxHeight) {
-                    val start = (constraints.maxWidth - placeable.measuredWidth) / 2
-                    placeable.placeRelative(x = start, y = notificationStackPosition.top.toInt())
-                }
-            }
-        }
-    }
-}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
index 65a53f5..9778e53 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
@@ -40,6 +40,7 @@
 import com.android.systemui.qs.ui.adapter.QSSceneAdapter.State.Expanding
 import com.android.systemui.res.R
 import com.android.systemui.scene.ui.composable.Gone
+import com.android.systemui.scene.ui.composable.Lockscreen
 import com.android.systemui.scene.ui.composable.QuickSettings as QuickSettingsSceneKey
 import com.android.systemui.scene.ui.composable.Shade
 
@@ -77,7 +78,12 @@
                     toScene == Shade -> QSSceneAdapter.State.QQS
                     toScene == QuickSettingsSceneKey -> QSSceneAdapter.State.QS
                     toScene == Gone -> QSSceneAdapter.State.CLOSED
-                    else -> error("Bad transition for QuickSettings: $transitionState")
+                    toScene == Lockscreen -> QSSceneAdapter.State.CLOSED
+                    else ->
+                        error(
+                            "Bad transition for QuickSettings: fromScene=$fromScene," +
+                                " toScene=$toScene"
+                        )
                 }
             }
     }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
index 99f81ee..e2beaee 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
@@ -91,12 +91,19 @@
     statusBarIconController: StatusBarIconController,
     modifier: Modifier = Modifier,
 ) {
-    val formatProgress = animateSceneFloatAsState(0.0f, ShadeHeader.Keys.transitionProgress)
+    val formatProgress =
+        animateSceneFloatAsState(0f, ShadeHeader.Keys.transitionProgress)
+            .unsafeCompositionState(initialValue = 0f)
 
     val cutoutWidth = LocalDisplayCutout.current.width()
     val cutoutLocation = LocalDisplayCutout.current.location
 
-    val useExpandedFormat = formatProgress.value > 0.5f || cutoutLocation != CutoutLocation.CENTER
+    val useExpandedFormat by
+        remember(formatProgress) {
+            derivedStateOf {
+                cutoutLocation != CutoutLocation.CENTER || formatProgress.value > 0.5f
+            }
+        }
 
     // This layout assumes it is globally positioned at (0, 0) and is the
     // same size as the screen.
@@ -209,7 +216,9 @@
     statusBarIconController: StatusBarIconController,
     modifier: Modifier = Modifier,
 ) {
-    val formatProgress = animateSceneFloatAsState(1.0f, ShadeHeader.Keys.transitionProgress)
+    val formatProgress =
+        animateSceneFloatAsState(1f, ShadeHeader.Keys.transitionProgress)
+            .unsafeCompositionState(initialValue = 1f)
     val useExpandedFormat by
         remember(formatProgress) { derivedStateOf { formatProgress.value > 0.5f } }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/SysuiTestCaseSelfTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/SysuiTestCaseSelfTest.kt
new file mode 100644
index 0000000..be6bb9c
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/SysuiTestCaseSelfTest.kt
@@ -0,0 +1,35 @@
+/*
+ * 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
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SysuiTestCaseSelfTest : SysuiTestCase() {
+    private val contextBeforeSetup = context
+
+    // cf b/311612168
+    @Test
+    fun captureCorrectContextBeforeSetupRuns() {
+        Truth.assertThat(contextBeforeSetup).isEqualTo(context)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractorTest.kt
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractorTest.kt
index 8adee8d..8d6d052 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractorTest.kt
@@ -24,6 +24,7 @@
 import android.view.WindowInsets
 import android.view.WindowManager
 import android.view.WindowMetrics
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider
@@ -61,7 +62,6 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Mock
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.spy
@@ -69,7 +69,7 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class SideFpsSensorInteractorTest : SysuiTestCase() {
     private val kosmos = testKosmos()
 
@@ -104,6 +104,7 @@
         contextDisplayInfo.uniqueId = "current-display"
         whenever(fingerprintInteractiveToAuthProvider.enabledForCurrentUser)
             .thenReturn(isRestToUnlockEnabled)
+        overrideResource(R.bool.config_restToUnlockSupported, true)
         underTest =
             SideFpsSensorInteractor(
                 mContext,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/common/data/repository/PackageChangeRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/common/data/repository/PackageChangeRepositoryTest.kt
new file mode 100644
index 0000000..6380ace
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/common/data/repository/PackageChangeRepositoryTest.kt
@@ -0,0 +1,209 @@
+/*
+ * 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.common.data.repository
+
+import android.content.Context
+import android.content.pm.PackageManager
+import android.os.Handler
+import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.data.shared.model.PackageChangeModel
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.log.logcatLogBuffer
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class PackageChangeRepositoryTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+
+    @Mock private lateinit var context: Context
+    @Mock private lateinit var packageManager: PackageManager
+    @Mock private lateinit var handler: Handler
+
+    private lateinit var repository: PackageChangeRepository
+    private lateinit var updateMonitor: PackageUpdateMonitor
+
+    @Before
+    fun setUp() =
+        with(kosmos) {
+            MockitoAnnotations.initMocks(this@PackageChangeRepositoryTest)
+            whenever(context.packageManager).thenReturn(packageManager)
+
+            repository = PackageChangeRepositoryImpl { user ->
+                updateMonitor =
+                    PackageUpdateMonitor(
+                        user = user,
+                        bgDispatcher = testDispatcher,
+                        scope = applicationCoroutineScope,
+                        context = context,
+                        bgHandler = handler,
+                        logger = PackageUpdateLogger(logcatLogBuffer())
+                    )
+                updateMonitor
+            }
+        }
+
+    @Test
+    fun packageUninstalled() =
+        with(kosmos) {
+            testScope.runTest {
+                val packageChange by collectLastValue(repository.packageChanged(USER_100))
+                assertThat(packageChange).isNull()
+
+                updateMonitor.onPackageRemoved(
+                    packageName = TEST_PACKAGE,
+                    uid = UserHandle.getUid(/* userId = */ 100, /* appId = */ 10)
+                )
+
+                assertThat(packageChange).isInstanceOf(PackageChangeModel.Uninstalled::class.java)
+                assertThat(packageChange?.packageName).isEqualTo(TEST_PACKAGE)
+            }
+        }
+
+    @Test
+    fun packageUpdateStarted() =
+        with(kosmos) {
+            testScope.runTest {
+                val packageChange by collectLastValue(repository.packageChanged(USER_100))
+                assertThat(packageChange).isNull()
+
+                updateMonitor.onPackageUpdateStarted(
+                    packageName = TEST_PACKAGE,
+                    uid = UserHandle.getUid(/* userId = */ 100, /* appId = */ 10)
+                )
+
+                assertThat(packageChange).isInstanceOf(PackageChangeModel.UpdateStarted::class.java)
+                assertThat(packageChange?.packageName).isEqualTo(TEST_PACKAGE)
+            }
+        }
+
+    @Test
+    fun packageUpdateFinished() =
+        with(kosmos) {
+            testScope.runTest {
+                val packageChange by collectLastValue(repository.packageChanged(USER_100))
+                assertThat(packageChange).isNull()
+
+                updateMonitor.onPackageUpdateFinished(
+                    packageName = TEST_PACKAGE,
+                    uid = UserHandle.getUid(/* userId = */ 100, /* appId = */ 10)
+                )
+
+                assertThat(packageChange)
+                    .isInstanceOf(PackageChangeModel.UpdateFinished::class.java)
+                assertThat(packageChange?.packageName).isEqualTo(TEST_PACKAGE)
+            }
+        }
+
+    @Test
+    fun packageInstalled() =
+        with(kosmos) {
+            testScope.runTest {
+                val packageChange by collectLastValue(repository.packageChanged(UserHandle.ALL))
+                assertThat(packageChange).isNull()
+
+                updateMonitor.onPackageAdded(
+                    packageName = TEST_PACKAGE,
+                    uid = UserHandle.getUid(/* userId = */ 100, /* appId = */ 10)
+                )
+
+                assertThat(packageChange).isInstanceOf(PackageChangeModel.Installed::class.java)
+                assertThat(packageChange?.packageName).isEqualTo(TEST_PACKAGE)
+            }
+        }
+
+    @Test
+    fun packageIsChanged() =
+        with(kosmos) {
+            testScope.runTest {
+                val packageChange by collectLastValue(repository.packageChanged(USER_100))
+                assertThat(packageChange).isNull()
+
+                updateMonitor.onPackageChanged(
+                    packageName = TEST_PACKAGE,
+                    uid = UserHandle.getUid(/* userId = */ 100, /* appId = */ 10),
+                    components = emptyArray()
+                )
+
+                assertThat(packageChange).isInstanceOf(PackageChangeModel.Changed::class.java)
+                assertThat(packageChange?.packageName).isEqualTo(TEST_PACKAGE)
+            }
+        }
+
+    @Test
+    fun filtersOutUpdatesFromOtherUsers() =
+        with(kosmos) {
+            testScope.runTest {
+                val packageChange by collectLastValue(repository.packageChanged(USER_100))
+                assertThat(packageChange).isNull()
+
+                updateMonitor.onPackageUpdateFinished(
+                    packageName = TEST_PACKAGE,
+                    uid = UserHandle.getUid(/* userId = */ 101, /* appId = */ 10)
+                )
+
+                updateMonitor.onPackageAdded(
+                    packageName = TEST_PACKAGE,
+                    uid = UserHandle.getUid(/* userId = */ 99, /* appId = */ 10)
+                )
+
+                assertThat(packageChange).isNull()
+            }
+        }
+
+    @Test
+    fun listenToUpdatesFromAllUsers() =
+        with(kosmos) {
+            testScope.runTest {
+                val packageChanges by collectValues(repository.packageChanged(UserHandle.ALL))
+                assertThat(packageChanges).isEmpty()
+
+                updateMonitor.onPackageUpdateFinished(
+                    packageName = TEST_PACKAGE,
+                    uid = UserHandle.getUid(/* userId = */ 101, /* appId = */ 10)
+                )
+
+                updateMonitor.onPackageAdded(
+                    packageName = TEST_PACKAGE,
+                    uid = UserHandle.getUid(/* userId = */ 99, /* appId = */ 10)
+                )
+
+                assertThat(packageChanges).hasSize(2)
+            }
+        }
+
+    private companion object {
+        val USER_100 = UserHandle.of(100)
+        const val TEST_PACKAGE = "pkg.test"
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/common/data/repository/PackageUpdateMonitorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/common/data/repository/PackageUpdateMonitorTest.kt
new file mode 100644
index 0000000..d610925
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/common/data/repository/PackageUpdateMonitorTest.kt
@@ -0,0 +1,210 @@
+/*
+ * 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.common.data.repository
+
+import android.content.Context
+import android.content.pm.PackageManager
+import android.os.Handler
+import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.data.shared.model.PackageChangeModel
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.log.logcatLogBuffer
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class PackageUpdateMonitorTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+
+    @Mock private lateinit var context: Context
+    @Mock private lateinit var packageManager: PackageManager
+    @Mock private lateinit var handler: Handler
+
+    private lateinit var monitor: PackageUpdateMonitor
+
+    @Before
+    fun setUp() =
+        with(kosmos) {
+            MockitoAnnotations.initMocks(this@PackageUpdateMonitorTest)
+            whenever(context.packageManager).thenReturn(packageManager)
+
+            monitor =
+                PackageUpdateMonitor(
+                    user = USER_100,
+                    bgDispatcher = testDispatcher,
+                    bgHandler = handler,
+                    context = context,
+                    scope = applicationCoroutineScope,
+                    logger = PackageUpdateLogger(logcatLogBuffer())
+                )
+        }
+
+    @Test
+    fun becomesActiveWhenFlowCollected() =
+        with(kosmos) {
+            testScope.runTest {
+                assertThat(monitor.isActive).isFalse()
+                val job = monitor.packageChanged.launchIn(this)
+                runCurrent()
+                assertThat(monitor.isActive).isTrue()
+                job.cancel()
+                runCurrent()
+                assertThat(monitor.isActive).isFalse()
+            }
+        }
+
+    @Test
+    fun packageAdded() =
+        with(kosmos) {
+            testScope.runTest {
+                val packageChange by collectLastValue(monitor.packageChanged)
+                assertThat(packageChange).isNull()
+
+                monitor.onPackageAdded(TEST_PACKAGE, 123)
+
+                assertThat(packageChange)
+                    .isEqualTo(
+                        PackageChangeModel.Installed(packageName = TEST_PACKAGE, packageUid = 123)
+                    )
+            }
+        }
+
+    @Test
+    fun packageRemoved() =
+        with(kosmos) {
+            testScope.runTest {
+                val packageChange by collectLastValue(monitor.packageChanged)
+                assertThat(packageChange).isNull()
+
+                monitor.onPackageRemoved(TEST_PACKAGE, 123)
+
+                assertThat(packageChange)
+                    .isEqualTo(
+                        PackageChangeModel.Uninstalled(packageName = TEST_PACKAGE, packageUid = 123)
+                    )
+            }
+        }
+
+    @Test
+    fun packageChanged() =
+        with(kosmos) {
+            testScope.runTest {
+                val packageChange by collectLastValue(monitor.packageChanged)
+                assertThat(packageChange).isNull()
+
+                monitor.onPackageChanged(TEST_PACKAGE, 123, emptyArray())
+
+                assertThat(packageChange)
+                    .isEqualTo(
+                        PackageChangeModel.Changed(packageName = TEST_PACKAGE, packageUid = 123)
+                    )
+            }
+        }
+
+    @Test
+    fun packageUpdateStarted() =
+        with(kosmos) {
+            testScope.runTest {
+                val packageChange by collectLastValue(monitor.packageChanged)
+                assertThat(packageChange).isNull()
+
+                monitor.onPackageUpdateStarted(TEST_PACKAGE, 123)
+
+                assertThat(packageChange)
+                    .isEqualTo(
+                        PackageChangeModel.UpdateStarted(
+                            packageName = TEST_PACKAGE,
+                            packageUid = 123
+                        )
+                    )
+            }
+        }
+
+    @Test
+    fun packageUpdateFinished() =
+        with(kosmos) {
+            testScope.runTest {
+                val packageChange by collectLastValue(monitor.packageChanged)
+                assertThat(packageChange).isNull()
+
+                monitor.onPackageUpdateFinished(TEST_PACKAGE, 123)
+
+                assertThat(packageChange)
+                    .isEqualTo(
+                        PackageChangeModel.UpdateFinished(
+                            packageName = TEST_PACKAGE,
+                            packageUid = 123
+                        )
+                    )
+            }
+        }
+
+    @Test
+    fun handlesBackflow() =
+        with(kosmos) {
+            testScope.runTest {
+                val latch = MutableSharedFlow<Unit>()
+                val packageChanges by collectValues(monitor.packageChanged.onEach { latch.first() })
+                assertThat(packageChanges).isEmpty()
+
+                monitor.onPackageAdded(TEST_PACKAGE, 123)
+                monitor.onPackageUpdateStarted(TEST_PACKAGE, 123)
+                monitor.onPackageUpdateFinished(TEST_PACKAGE, 123)
+
+                assertThat(packageChanges).isEmpty()
+                latch.emit(Unit)
+                assertThat(packageChanges).hasSize(1)
+                latch.emit(Unit)
+                assertThat(packageChanges).hasSize(2)
+                latch.emit(Unit)
+                assertThat(packageChanges)
+                    .containsExactly(
+                        PackageChangeModel.Installed(TEST_PACKAGE, 123),
+                        PackageChangeModel.UpdateStarted(TEST_PACKAGE, 123),
+                        PackageChangeModel.UpdateFinished(TEST_PACKAGE, 123),
+                    )
+                    .inOrder()
+            }
+        }
+
+    companion object {
+        private val USER_100 = UserHandle.of(100)
+        private const val TEST_PACKAGE = "pkg.test"
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
index ddaa488..449ee6f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
@@ -42,6 +42,7 @@
 import com.android.systemui.util.mockito.nullable
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
+import java.util.Optional
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.flowOf
@@ -63,6 +64,8 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
+    @Mock private lateinit var appWidgetManagerOptional: Optional<AppWidgetManager>
+
     @Mock private lateinit var appWidgetManager: AppWidgetManager
 
     @Mock private lateinit var appWidgetHost: AppWidgetHost
@@ -113,6 +116,8 @@
         whenever(stopwatchProviderInfo.loadLabel(any())).thenReturn("Stopwatch")
         whenever(userTracker.userHandle).thenReturn(userHandle)
         whenever(communalWidgetDao.getWidgets()).thenReturn(flowOf(emptyMap()))
+        whenever(appWidgetManagerOptional.isPresent).thenReturn(true)
+        whenever(appWidgetManagerOptional.get()).thenReturn(appWidgetManager)
     }
 
     @Test
@@ -296,7 +301,7 @@
 
     private fun initCommunalWidgetRepository(): CommunalWidgetRepositoryImpl {
         return CommunalWidgetRepositoryImpl(
-            appWidgetManager,
+            appWidgetManagerOptional,
             appWidgetHost,
             testScope.backgroundScope,
             testDispatcher,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt
index 90779cb..20653ca 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt
@@ -55,6 +55,7 @@
     private val underTest: CustomTileInteractor =
         with(kosmos) {
             CustomTileInteractor(
+                tileSpec,
                 customTileDefaultsRepository,
                 customTileRepository,
                 testScope.backgroundScope,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorTest.kt
similarity index 70%
copy from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
copy to packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorTest.kt
index efc7431..dbff63f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorTest.kt
@@ -14,12 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.di.bound
+package com.android.systemui.volume.panel.domain.interactor
 
-import javax.inject.Qualifier
+class ComponentsInteractorTest {
 
-/** User associated with current custom tile binding. */
-@Qualifier
-@MustBeDocumented
-@Retention(AnnotationRetention.RUNTIME)
-annotation class CustomTileUser
+    // TODO(b/318080198) Write tests
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/DefaultComponentsLayoutManagerTest.kt
similarity index 70%
copy from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
copy to packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/DefaultComponentsLayoutManagerTest.kt
index efc7431..e5fb942 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/DefaultComponentsLayoutManagerTest.kt
@@ -14,12 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.di.bound
+package com.android.systemui.volume.panel.ui.viewmodel
 
-import javax.inject.Qualifier
+class DefaultComponentsLayoutManagerTest {
 
-/** User associated with current custom tile binding. */
-@Qualifier
-@MustBeDocumented
-@Retention(AnnotationRetention.RUNTIME)
-annotation class CustomTileUser
+    // TODO(b/318080198) Write tests
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt
similarity index 70%
copy from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
copy to packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt
index efc7431..9795237 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt
@@ -14,12 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.di.bound
+package com.android.systemui.volume.panel.ui.viewmodel
 
-import javax.inject.Qualifier
+class VolumePanelViewModelTest {
 
-/** User associated with current custom tile binding. */
-@Qualifier
-@MustBeDocumented
-@Retention(AnnotationRetention.RUNTIME)
-annotation class CustomTileUser
+    // TODO(b/318080198) Write tests
+}
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 74e92ba..40fddc8 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"በቅንብሮች ውስጥ ነባሪ የማስታወሻዎች መተግበሪያን ያቀናብሩ"</string>
     <string name="install_app" msgid="5066668100199613936">"መተግበሪያን ጫን"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ወደ ውጫዊ ማሳያ ይንጸባረቅ?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"የውስጥ ማሳያዎ ይንጸባረቃል። የፊት ማሳያዎ ይጠፋል።"</string>
     <string name="mirror_display" msgid="2515262008898122928">"ማሳያን አንጸባርቅ"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"አሰናብት"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"ማሳያ ተገናኝቷል"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 8aeeaf7..d19c77b 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -330,12 +330,9 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"تسجيل الشاشة"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"بدء"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"إيقاف"</string>
-    <!-- no translation found for qs_record_issue_label (8166290137285529059) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_start (2979831312582567056) -->
-    <skip />
-    <!-- no translation found for qs_record_issue_stop (3531747965741982657) -->
-    <skip />
+    <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>
     <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>
@@ -416,12 +413,9 @@
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • جارٍ الشحن • ستمتلئ البطارية خلال <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"مرِّر سريعًا لليمين لبدء الدليل التوجيهي العام."</string>
     <string name="button_to_open_widget_editor" msgid="5599945944349057600">"فتح محرِّر التطبيقات المصغّرة"</string>
-    <!-- no translation found for button_to_remove_widget (3948204829181214098) -->
-    <skip />
-    <!-- no translation found for hub_mode_add_widget_button_text (4831464661209971729) -->
-    <skip />
-    <!-- no translation found for hub_mode_editing_exit_button_text (3704686734192264771) -->
-    <skip />
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"إزالة"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"إضافة تطبيق مصغّر"</string>
+    <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"تم"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"تبديل المستخدم"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"القائمة المنسدلة"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"سيتم حذف كل التطبيقات والبيانات في هذه الجلسة."</string>
@@ -1214,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"يمكنك ضبط تطبيق تدوين الملاحظات التلقائي في \"الإعدادات\"."</string>
     <string name="install_app" msgid="5066668100199613936">"تثبيت التطبيق"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"هل تريد بث محتوى جهازك على الشاشة الخارجية؟"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"سيتم النسخ المطابق لمحتوى الشاشة الداخلية، وإيقاف الشاشة الأمامية."</string>
     <string name="mirror_display" msgid="2515262008898122928">"بث المحتوى على الشاشة"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"إغلاق"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"تم توصيل الشاشة"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index a1b5389..4e78f55 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ছেটিঙত টোকাৰ ডিফ’ল্ট এপ্ ছেট কৰক"</string>
     <string name="install_app" msgid="5066668100199613936">"এপ্‌টো ইনষ্টল কৰক"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"বাহ্যিক ডিছপ্লে’লৈ মিৰ’ৰ কৰিবনে?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"আপোনাৰ ইনাৰ ডিছপ্লে’ প্ৰতিবিম্বিত কৰা হ’ব। আপোনাৰ ফ্ৰণ্ট ডিছপ্লে’ অফ কৰা হ’ব।"</string>
     <string name="mirror_display" msgid="2515262008898122928">"ডিছপ্লে’ মিৰ’ৰ কৰক"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"অগ্ৰাহ্য কৰক"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"ডিছপ্লে’ সংযোগ কৰা হৈছে"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 2a9a9cf..bf32c5e 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Ayarlarda defolt qeydlər tətbiqi ayarlayın"</string>
     <string name="install_app" msgid="5066668100199613936">"Tətbiqi quraşdırın"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Xarici displeyə əks etdirilsin?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"İç displey əks etdiriləcək. Ön ekran deaktiv ediləcək."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Displeyi əks etdirin"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"İmtina edin"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Displey qoşulub"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 47468ff..56c3e45 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Podesite podrazumevanu aplikaciju za beleške u Podešavanjima"</string>
     <string name="install_app" msgid="5066668100199613936">"Instaliraj aplikaciju"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Želite li da preslikate na spoljnji ekran?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Unutrašnji ekran će se preslikati. Prednji ekran će se isključiti."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Preslikaj ekran"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Odbaci"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Ekran je povezan"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 1fe96a6..22d167e 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Задайте стандартно приложение за бележки от настройките"</string>
     <string name="install_app" msgid="5066668100199613936">"Инсталиране на приложението"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Да се дублира ли на външния екран?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Съдържанието на вътрешния ви дисплей ще бъде дублирано. Предният ви дисплей ще бъде изключен."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Дублиране на дисплея"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Отхвърляне"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Свързан е екран"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index fa30131..045af93 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"\'সেটিংস\' থেকে ডিফল্ট নোট নেওয়ার অ্যাপ সেট করুন"</string>
     <string name="install_app" msgid="5066668100199613936">"অ্যাপ ইনস্টল করুন"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"এক্সটার্নাল ডিসপ্লেতে মিরর করবেন?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"আপনার ইনার ডিসপ্লে মিরর করা হবে। আপনার ফ্রন্ট ডিসপ্লে বন্ধ করা হবে।"</string>
     <string name="mirror_display" msgid="2515262008898122928">"ডিসপ্লে দেখান"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"বাতিল করুন"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"ডিসপ্লে কানেক্ট করা আছে"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index db5987fb..ae962ce 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Postavite zadanu aplikaciju za bilješke u Postavkama"</string>
     <string name="install_app" msgid="5066668100199613936">"Instaliraj aplikaciju"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Preslikati na vanjski ekran?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Unutrašnji ekran će se preslikavati. Prednji ekran će se isključiti."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Preslikaj ekran"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Odbaci"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Ekran je povezan"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index af4492b..8cf8828 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -1207,10 +1207,9 @@
     <string name="assistant_attention_content_description" msgid="4166330881435263596">"S\'ha detectat la presència d\'usuaris"</string>
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Defineix l\'aplicació de notes predeterminada a Configuració"</string>
     <string name="install_app" msgid="5066668100199613936">"Instal·la l\'aplicació"</string>
-    <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Replicar a la pantalla externa?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
-    <string name="mirror_display" msgid="2515262008898122928">"Replica la pantalla"</string>
+    <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Duplicar a la pantalla externa?"</string>
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"La pantalla interior es duplicarà. La pantalla frontal es desactivarà."</string>
+    <string name="mirror_display" msgid="2515262008898122928">"Duplica la pantalla"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Ignora"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Pantalla connectada"</string>
     <string name="privacy_dialog_title" msgid="7839968133469098311">"Micròfon i càmera"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 25fcf99..17084dc 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Výchozí aplikaci pro poznámky nastavíte v Nastavení"</string>
     <string name="install_app" msgid="5066668100199613936">"Nainstalovat aplikaci"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Zrcadlit na externí displej?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Vnitřní displej bude zrcadlen. Přední displej bude vypnutý."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Zrcadlit displej"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Zavřít"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Displej připojen"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 91c223a..41abea3 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Angiv standardapp til noter i Indstillinger"</string>
     <string name="install_app" msgid="5066668100199613936">"Installer app"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Vil du spejle til ekstern skærm?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Din indre skærm spejles. Din skærm på forsiden slukkes."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Spejl skærm"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Luk"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Skærmen er tilsluttet"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 29b7018..d95e229 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Standard-Notizen-App in den Einstellungen einrichten"</string>
     <string name="install_app" msgid="5066668100199613936">"App installieren"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Auf externen Bildschirm spiegeln?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Dein inneres Display wird gespiegelt. Das Frontdisplay wird ausgeschaltet."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Bildschirm spiegeln"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Schließen"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Bildschirm verbunden"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index dd24ca6..5848e4f 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Ορίστε την προεπιλεγμένη εφαρμογή σημειώσεων στις Ρυθμίσεις"</string>
     <string name="install_app" msgid="5066668100199613936">"Εγκατάσταση εφαρμογής"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Κατοπτρισμός σε εξωτερική οθόνη;"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Θα γίνει κατοπτρισμός της εσωτερικής προβολής. Η μπροστινή οθόνη θα απενεργοποιηθεί."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Κατοπτρισμός οθόνης"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Παράβλεψη"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Η οθόνη είναι συνδεδεμένη"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 2d05291..870e4dd 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Set default notes app in Settings"</string>
     <string name="install_app" msgid="5066668100199613936">"Install app"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Mirror to external display?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Your inner display will be mirrored. Your front display will be turned off."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Mirror display"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Dismiss"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Display connected"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index c043b2c..f25baf2 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Set default notes app in Settings"</string>
     <string name="install_app" msgid="5066668100199613936">"Install app"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Mirror to external display?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Your inner display will be mirrored. Your front display will be turned off."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Mirror display"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Dismiss"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Display connected"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 2d05291..870e4dd 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Set default notes app in Settings"</string>
     <string name="install_app" msgid="5066668100199613936">"Install app"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Mirror to external display?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Your inner display will be mirrored. Your front display will be turned off."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Mirror display"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Dismiss"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Display connected"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 2d05291..870e4dd 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Set default notes app in Settings"</string>
     <string name="install_app" msgid="5066668100199613936">"Install app"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Mirror to external display?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Your inner display will be mirrored. Your front display will be turned off."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Mirror display"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Dismiss"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Display connected"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 3691b2c..b3ed714 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‎‎‎‎‎‏‏‏‏‎‎‎‏‏‏‏‏‎‎‎‏‏‎‏‎‏‎‏‏‏‏‎‏‏‏‏‎‏‏‏‏‎‎‏‏‏‎‏‎‏‎‎‎‎‎‏‎Set default notes app in Settings‎‏‎‎‏‎"</string>
     <string name="install_app" msgid="5066668100199613936">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‏‎‏‎‎‎‎‎‏‏‎‏‎‏‏‏‏‎‎‏‎‏‎‏‏‏‏‎‎‏‏‎‎‏‏‎‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‎‎‎‎‎Install app‎‏‎‎‏‎"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‎‏‎‎‏‎‏‎‏‏‏‏‎‏‎‎‎‎‎‎‏‎‎‎‏‏‎‎‏‎‎‎‏‏‏‏‎‎‎‏‏‎‎Mirror to external display?‎‏‎‎‏‎"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‏‏‎‏‏‏‏‏‎‎‎‏‎‏‎‎‏‎‏‏‎‏‎‎‎‎‎‏‎‎‎‏‎‏‎‎‏‎‎‏‏‎‏‎‎‎‎‏‏‎‎‏‎‏‏‎Your inner display will be mirrored. Your front display will be turned off.‎‏‎‎‏‎"</string>
     <string name="mirror_display" msgid="2515262008898122928">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‏‏‏‎‏‎‎‎‎‎‎‎‎‎‎‏‎‏‏‏‏‎‎‎‎‏‏‏‏‏‎‏‎‏‏‎‏‎‎‎‎‏‏‏‎‏‎‎‏‎‏‏‎‎‎‎‎Mirror display‎‏‎‎‏‎"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‏‏‎‎‎‎‎‎‎‎‎‏‏‎‏‎‏‏‎‎‎‎‎‏‏‏‏‎‎‏‏‎‎‎‏‎‏‎‎‏‎‎‏‏‏‏‎‏‎‏‎‏‎‎Dismiss‎‏‎‎‏‎"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‏‎‎‏‏‎‏‏‎‏‏‏‏‏‎‏‎‎‎‏‎‎‎‏‎‏‏‎‏‎‎‎‎‎‎‏‏‎‏‏‏‏‏‎‎‎‎‎‏‏‏‎‏‎Display connected‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 6a4818e..c535560 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Configura la app de notas predeterminada en Configuración"</string>
     <string name="install_app" msgid="5066668100199613936">"Instalar app"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"¿Quieres duplicar en la pantalla externa?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Se duplicará la pantalla interior. Se apagará la pantalla frontal."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Duplicar pantalla"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Descartar"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Pantalla conectada"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 4e41db3..1157ff1 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Configura la aplicación de notas predeterminada en Ajustes"</string>
     <string name="install_app" msgid="5066668100199613936">"Instalar aplicación"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"¿Proyectar a pantalla externa?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Se proyectará tu pantalla interior. Se apagará tu pantalla frontal."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Proyectar pantalla"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Cerrar"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Pantalla conectada"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 231ddeb..36b3f1e 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Määrake seadetes märkmete vaikerakendus."</string>
     <string name="install_app" msgid="5066668100199613936">"Installi rakendus"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Kas peegeldada välisekraanile?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Teie siseekraani peegeldatakse. Teie esiekraan lülitatakse välja."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Peegelda ekraani"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Loobu"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Kuvar on ühendatud"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 20bf71b..5ea02d1 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Ezarri oharren aplikazio lehenetsia ezarpenetan"</string>
     <string name="install_app" msgid="5066668100199613936">"Instalatu aplikazioa"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Kanpoko pantailan islatu nahi duzu?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Barneko pantaila islatuko da. Aurreko pantaila desaktibatu egingo da."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Islatu pantaila"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Baztertu"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Konektatutako pantaila"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index e161d7a..aa77b4b 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"برنامه پیش‌فرض یادداشت را در «تنظیمات» تنظیم کنید"</string>
     <string name="install_app" msgid="5066668100199613936">"نصب برنامه"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"روی نمایشگر خارجی قرینه‌سازی شود؟"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"نمایشگر داخلی شما قرینه‌سازی می‌شود. نمایشگر جلو خاموش می‌شود."</string>
     <string name="mirror_display" msgid="2515262008898122928">"قرینه‌سازی نمایشگر"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"بستن"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"نمایشگر متصل شد"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 240607b..882c42c 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Aseta oletusmuistiinpanosovellus Asetuksista"</string>
     <string name="install_app" msgid="5066668100199613936">"Asenna sovellus"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Peilataanko ulkoiselle näytölle?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Sisänäyttö peilataan. Etunäyttö laitetaan pois päältä."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Peilaa näyttö"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Ohita"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Näyttö yhdistetty"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 82a56f5..8370b01 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Définir l\'application de prise de notes par défaut dans les Paramètres"</string>
     <string name="install_app" msgid="5066668100199613936">"Installer l\'application"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Dupliquer l\'écran sur un moniteur externe?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Votre écran intérieur sera dupliqué. Votre écran frontal sera désactivé."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Dupliquer l\'écran"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Fermer"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Écran connecté"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index c35f9ee..af81a31 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Définir une appli de notes par défaut dans les paramètres"</string>
     <string name="install_app" msgid="5066668100199613936">"Installer l\'appli"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Dupliquer sur l\'écran externe ?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Votre écran intérieur sera dupliqué. Votre écran frontal sera éteint."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Dupliquer l\'écran"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Fermer"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Écran connecté"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 9cf968e..ef0751b 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Establece a aplicación de notas predeterminada en Configuración"</string>
     <string name="install_app" msgid="5066668100199613936">"Instalar aplicación"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Queres proxectar contido nunha pantalla externa?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Proxectarase a pantalla interior. Desactivarase a pantalla frontal."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Proxectar pantalla"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Pechar"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Pantalla conectada"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index df7b203..d92231c 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"સેટિંગમાં નોંધની ડિફૉલ્ટ ઍપ સેટ કરો"</string>
     <string name="install_app" msgid="5066668100199613936">"ઍપ ઇન્સ્ટૉલ કરો"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"શું બાહ્ય ડિસ્પ્લે પર મિરર કરીએ?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"તમારું ઇનર ડિસ્પ્લે મિરર કરવામાં આવશે. તમારું ફ્રન્ટ ડિસ્પ્લે બંધ કરવામાં આવશે."</string>
     <string name="mirror_display" msgid="2515262008898122928">"મિરર ડિસ્પ્લે"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"છોડી દો"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Display કનેક્ટેડ છે"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index eec3078..f8c78a9 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"सेटिंग में जाकर, नोट लेने की सुविधा देने वाले ऐप्लिकेशन को डिफ़ॉल्ट के तौर पर सेट करें"</string>
     <string name="install_app" msgid="5066668100199613936">"ऐप्लिकेशन इंस्टॉल करें"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"क्या आपको किसी बाहरी डिवाइस पर डिसप्ले करना है?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"आपके फ़ोन के इनर डिसप्ले की स्क्रीन शेयर की जाएगी. फ़्रंट डिसप्ले को बंद कर दिया जाएगा."</string>
     <string name="mirror_display" msgid="2515262008898122928">"डिसप्ले करें"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"खारिज करें"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"डिसप्ले कनेक्ट किया गया"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index f0ed400..0803aeb 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Postavite zadanu aplikaciju za bilješke u postavkama"</string>
     <string name="install_app" msgid="5066668100199613936">"Instalacija"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Želite li zrcaliti na vanjski zaslon?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Unutarnji zaslon bit će zrcaljen. Prednji zaslon bit će isključen."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Zrcaljenje zaslona"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Odbaci"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Zaslon je povezan"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 30d2961..fd9d832 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Állítson be alapértelmezett jegyzetkészítő alkalmazást a Beállításokban"</string>
     <string name="install_app" msgid="5066668100199613936">"Alkalmazás telepítése"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Tükrözi a kijelzőt a külső képernyőre?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"A belső kijelző tükrözve lesz. Az elülső kijelző ki lesz kapcsolva."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Kijelző tükrözése"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Elvetés"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Kijelző csatlakoztatva"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index bbdeb16..a9252e2 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Կարգավորեք նշումների կանխադրված հավելված Կարգավորումներում"</string>
     <string name="install_app" msgid="5066668100199613936">"Տեղադրել հավելվածը"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Հայելապատճենե՞լ արտաքին էկրանին"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Ներքին էկրանը կհայելապատճենվի։ Առջևի էկրանը կանջատվի։"</string>
     <string name="mirror_display" msgid="2515262008898122928">"Հայելապատճենել էկրանը"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Փակել"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Էկրանը միացած է"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index f2ed0e7..8ef809a 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Setel aplikasi catatan default di Setelan"</string>
     <string name="install_app" msgid="5066668100199613936">"Instal aplikasi"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Cerminkan ke layar eksternal?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Layar dalam akan dicerminkan. Layar depan akan dinonaktifkan."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Cerminkan layar"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Tutup"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Layar terhubung"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index f1f1b0f..cef3285 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Stilltu sjálfgefið glósuforrit í stillingunum"</string>
     <string name="install_app" msgid="5066668100199613936">"Setja upp forrit"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Spegla yfir á ytri skjá?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Innri skjárinn þinn verður speglaður. Slökkt verður á framskjánum þínum."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Spegla skjá"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Hunsa"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Skjár tengdur"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 223e39f..c5079fe 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Imposta l\'app per le note predefinita nelle Impostazioni"</string>
     <string name="install_app" msgid="5066668100199613936">"Installa app"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Vuoi eseguire il mirroring al display esterno?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Verrà eseguito il mirroring del tuo display interno. Il tuo display frontale verrà spento."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Esegui il mirroring del display"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Chiudi"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Display collegato"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 44fd608..486b22f 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"צריך להגדיר את אפליקציית ברירת המחדל לפתקים ב\'הגדרות\'"</string>
     <string name="install_app" msgid="5066668100199613936">"התקנת האפליקציה"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"לשקף למסך חיצוני?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"המסך הפנימי שלך ישוכפל. המסך החיצוני שלך יכובה."</string>
     <string name="mirror_display" msgid="2515262008898122928">"תצוגת מראה"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"סגירה"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"המסך מחובר"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 38f2dc8..c742f93 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"[設定] でデフォルトのメモアプリを設定してください"</string>
     <string name="install_app" msgid="5066668100199613936">"アプリをインストール"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"外部ディスプレイにミラーリングしますか?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"インナー ディスプレイがミラーリングされます。フロント ディスプレイは OFF になります。"</string>
     <string name="mirror_display" msgid="2515262008898122928">"ディスプレイをミラーリングする"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"閉じる"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"ディスプレイに接続しました"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index bb21f3f..9d386ef 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"დააყენეთ ნაგულისხმევი შენიშვნების აპი პარამეტრებში"</string>
     <string name="install_app" msgid="5066668100199613936">"აპის ინსტალაცია"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"აირეკლოს გარე ეკრანზე?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"თქვენი შიდა ეკრანი აირეკლება. თქვენი წინა ეკრანი გამოირთვება."</string>
     <string name="mirror_display" msgid="2515262008898122928">"ეკრანის არეკვლა"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"დახურვა"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"ეკრანი დაკავშირებულია"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index b39d5a5..d2c60f1 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Параметрлерден әдепкі жазба қолданбасын орнатыңыз."</string>
     <string name="install_app" msgid="5066668100199613936">"Қолданбаны орнату"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Сыртқы экран арқылы да көрсету керек пе?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Ішкі экран көшірмесі көрсетіледі. Алдыңғы экран өшіріледі."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Көрсету"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Жабу"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Дисплей қосылды"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index e989a9d..ec81523 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"កំណត់កម្មវិធីកំណត់ចំណាំលំនាំដើមនៅក្នុងការកំណត់"</string>
     <string name="install_app" msgid="5066668100199613936">"ដំឡើង​កម្មវិធី"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"បញ្ចាំងទៅ​ផ្ទាំងអេក្រង់​ខាងក្រៅឬ?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"អេក្រង់ខាងក្នុងរបស់អ្នកនឹងត្រូវបានធ្វើ​សមកាលកម្មទៅវិញទៅមក។ អេក្រង់មុខរបស់អ្នកនឹងត្រូវបានបិទ។"</string>
     <string name="mirror_display" msgid="2515262008898122928">"បញ្ចាំងទៅផ្ទាំងអេក្រង់"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"ច្រានចោល"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"ផ្ទាំងអេក្រង់ត្រូវបានភ្ជាប់"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 39b1468..33f4528 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -93,7 +93,7 @@
     <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>
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ಸೆಟ್ಟಿಂಗ್‌ಗಳಲ್ಲಿ ಡೀಫಾಲ್ಟ್ ಟಿಪ್ಪಣಿಗಳ ಆ್ಯಪ್ ಅನ್ನು ಸೆಟ್ ಮಾಡಿ"</string>
     <string name="install_app" msgid="5066668100199613936">"ಆ್ಯಪ್ ಇನ್‌ಸ್ಟಾಲ್ ಮಾಡಿ"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ಬಾಹ್ಯ ಡಿಸ್‌ಪ್ಲೇಗೆ ಪ್ರತಿಬಿಂಬಿಸಬೇಕೆ?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"ನಿಮ್ಮ ಒಳಗಿನ ಡಿಸ್‌ಪ್ಲೇ ಅನ್ನು ಪ್ರತಿಬಿಂಬಿಸಲಾಗುತ್ತದೆ. ನಿಮ್ಮ ಫ್ರಂಟ್ ಡಿಸ್‌ಪ್ಲೇ ಅನ್ನು ಆಫ್ ಮಾಡಲಾಗುತ್ತದೆ."</string>
     <string name="mirror_display" msgid="2515262008898122928">"ಮಿರರ್ ಡಿಸ್‌ಪ್ಲೇ"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"ವಜಾಗೊಳಿಸಿ"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"ಡಿಸ್‌ಪ್ಲೇ ಕನೆಕ್ಟ್ ಆಗಿದೆ"</string>
diff --git a/packages/SystemUI/res/values-kn/tiles_states_strings.xml b/packages/SystemUI/res/values-kn/tiles_states_strings.xml
index 16e82ea..876562d 100644
--- a/packages/SystemUI/res/values-kn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-kn/tiles_states_strings.xml
@@ -33,147 +33,147 @@
     <!-- no translation found for tile_states_default:2 (9192445505551219506) -->
   <string-array name="tile_states_internet">
     <item msgid="5499482407653291407">"ಲಭ್ಯವಿಲ್ಲ"</item>
-    <item msgid="3048856902433862868">"ಆಫ್ ಮಾಡಿ"</item>
+    <item msgid="3048856902433862868">"ಆಫ್"</item>
     <item msgid="6877982264300789870">"ಆನ್ ಮಾಡಿ"</item>
   </string-array>
   <string-array name="tile_states_wifi">
     <item msgid="8054147400538405410">"ಲಭ್ಯವಿಲ್ಲ"</item>
-    <item msgid="4293012229142257455">"ಆಫ್ ಮಾಡಿ"</item>
+    <item msgid="4293012229142257455">"ಆಫ್"</item>
     <item msgid="6221288736127914861">"ಆನ್ ಮಾಡಿ"</item>
   </string-array>
   <string-array name="tile_states_cell">
     <item msgid="1235899788959500719">"ಲಭ್ಯವಿಲ್ಲ"</item>
-    <item msgid="2074416252859094119">"ಆಫ್ ಮಾಡಿ"</item>
+    <item msgid="2074416252859094119">"ಆಫ್"</item>
     <item msgid="287997784730044767">"ಆನ್ ಮಾಡಿ"</item>
   </string-array>
   <string-array name="tile_states_battery">
     <item msgid="6311253873330062961">"ಲಭ್ಯವಿಲ್ಲ"</item>
-    <item msgid="7838121007534579872">"ಆಫ್ ಮಾಡಿ"</item>
+    <item msgid="7838121007534579872">"ಆಫ್"</item>
     <item msgid="1578872232501319194">"ಆನ್ ಮಾಡಿ"</item>
   </string-array>
   <string-array name="tile_states_dnd">
     <item msgid="467587075903158357">"ಲಭ್ಯವಿಲ್ಲ"</item>
-    <item msgid="5376619709702103243">"ಆಫ್ ಮಾಡಿ"</item>
+    <item msgid="5376619709702103243">"ಆಫ್"</item>
     <item msgid="4875147066469902392">"ಆನ್ ಮಾಡಿ"</item>
   </string-array>
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"ಲಭ್ಯವಿಲ್ಲ"</item>
-    <item msgid="5044688398303285224">"ಆಫ್ ಮಾಡಿ"</item>
+    <item msgid="5044688398303285224">"ಆಫ್"</item>
     <item msgid="8527389108867454098">"ಆನ್ ಮಾಡಿ"</item>
   </string-array>
   <string-array name="tile_states_rotation">
     <item msgid="4578491772376121579">"ಲಭ್ಯವಿಲ್ಲ"</item>
-    <item msgid="5776427577477729185">"ಆಫ್ ಮಾಡಿ"</item>
+    <item msgid="5776427577477729185">"ಆಫ್"</item>
     <item msgid="7105052717007227415">"ಆನ್ ಮಾಡಿ"</item>
   </string-array>
   <string-array name="tile_states_bt">
     <item msgid="5330252067413512277">"ಲಭ್ಯವಿಲ್ಲ"</item>
-    <item msgid="5315121904534729843">"ಆಫ್ ಮಾಡಿ"</item>
+    <item msgid="5315121904534729843">"ಆಫ್"</item>
     <item msgid="503679232285959074">"ಆನ್ ಮಾಡಿ"</item>
   </string-array>
   <string-array name="tile_states_airplane">
     <item msgid="1985366811411407764">"ಲಭ್ಯವಿಲ್ಲ"</item>
-    <item msgid="4801037224991420996">"ಆಫ್ ಮಾಡಿ"</item>
+    <item msgid="4801037224991420996">"ಆಫ್"</item>
     <item msgid="1982293347302546665">"ಆನ್ ಮಾಡಿ"</item>
   </string-array>
   <string-array name="tile_states_location">
     <item msgid="3316542218706374405">"ಲಭ್ಯವಿಲ್ಲ"</item>
-    <item msgid="4813655083852587017">"ಆಫ್ ಮಾಡಿ"</item>
+    <item msgid="4813655083852587017">"ಆಫ್"</item>
     <item msgid="6744077414775180687">"ಆನ್ ಮಾಡಿ"</item>
   </string-array>
   <string-array name="tile_states_hotspot">
     <item msgid="3145597331197351214">"ಲಭ್ಯವಿಲ್ಲ"</item>
-    <item msgid="5715725170633593906">"ಆಫ್ ಮಾಡಿ"</item>
+    <item msgid="5715725170633593906">"ಆಫ್"</item>
     <item msgid="2075645297847971154">"ಆನ್ ಮಾಡಿ"</item>
   </string-array>
   <string-array name="tile_states_color_correction">
     <item msgid="2840507878437297682">"ಲಭ್ಯವಿಲ್ಲ"</item>
-    <item msgid="1909756493418256167">"ಆಫ್ ಮಾಡಿ"</item>
+    <item msgid="1909756493418256167">"ಆಫ್"</item>
     <item msgid="4531508423703413340">"ಆನ್ ಮಾಡಿ"</item>
   </string-array>
   <string-array name="tile_states_inversion">
     <item msgid="3638187931191394628">"ಲಭ್ಯವಿಲ್ಲ"</item>
-    <item msgid="9103697205127645916">"ಆಫ್ ಮಾಡಿ"</item>
+    <item msgid="9103697205127645916">"ಆಫ್"</item>
     <item msgid="8067744885820618230">"ಆನ್ ಮಾಡಿ"</item>
   </string-array>
   <string-array name="tile_states_saver">
     <item msgid="39714521631367660">"ಲಭ್ಯವಿಲ್ಲ"</item>
-    <item msgid="6983679487661600728">"ಆಫ್ ಮಾಡಿ"</item>
+    <item msgid="6983679487661600728">"ಆಫ್"</item>
     <item msgid="7520663805910678476">"ಆನ್ ಮಾಡಿ"</item>
   </string-array>
   <string-array name="tile_states_dark">
     <item msgid="2762596907080603047">"ಲಭ್ಯವಿಲ್ಲ"</item>
-    <item msgid="400477985171353">"ಆಫ್ ಮಾಡಿ"</item>
+    <item msgid="400477985171353">"ಆಫ್"</item>
     <item msgid="630890598801118771">"ಆನ್ ಮಾಡಿ"</item>
   </string-array>
   <string-array name="tile_states_work">
     <item msgid="389523503690414094">"ಲಭ್ಯವಿಲ್ಲ"</item>
-    <item msgid="8045580926543311193">"ಆಫ್ ಮಾಡಿ"</item>
+    <item msgid="8045580926543311193">"ಆಫ್"</item>
     <item msgid="4913460972266982499">"ಆನ್ ಮಾಡಿ"</item>
   </string-array>
   <string-array name="tile_states_cast">
     <item msgid="6032026038702435350">"ಲಭ್ಯವಿಲ್ಲ"</item>
-    <item msgid="1488620600954313499">"ಆಫ್ ಮಾಡಿ"</item>
+    <item msgid="1488620600954313499">"ಆಫ್"</item>
     <item msgid="588467578853244035">"ಆನ್ ಮಾಡಿ"</item>
   </string-array>
   <string-array name="tile_states_night">
     <item msgid="7857498964264855466">"ಲಭ್ಯವಿಲ್ಲ"</item>
-    <item msgid="2744885441164350155">"ಆಫ್ ಮಾಡಿ"</item>
+    <item msgid="2744885441164350155">"ಆಫ್"</item>
     <item msgid="151121227514952197">"ಆನ್ ಮಾಡಿ"</item>
   </string-array>
   <string-array name="tile_states_screenrecord">
     <item msgid="1085836626613341403">"ಲಭ್ಯವಿಲ್ಲ"</item>
-    <item msgid="8259411607272330225">"ಆಫ್ ಮಾಡಿ"</item>
+    <item msgid="8259411607272330225">"ಆಫ್"</item>
     <item msgid="578444932039713369">"ಆನ್ ಮಾಡಿ"</item>
   </string-array>
   <string-array name="tile_states_reverse">
     <item msgid="3574611556622963971">"ಲಭ್ಯವಿಲ್ಲ"</item>
-    <item msgid="8707481475312432575">"ಆಫ್ ಮಾಡಿ"</item>
+    <item msgid="8707481475312432575">"ಆಫ್"</item>
     <item msgid="8031106212477483874">"ಆನ್ ಮಾಡಿ"</item>
   </string-array>
   <string-array name="tile_states_reduce_brightness">
     <item msgid="1839836132729571766">"ಲಭ್ಯವಿಲ್ಲ"</item>
-    <item msgid="4572245614982283078">"ಆಫ್ ಮಾಡಿ"</item>
+    <item msgid="4572245614982283078">"ಆಫ್"</item>
     <item msgid="6536448410252185664">"ಆನ್ ಮಾಡಿ"</item>
   </string-array>
   <string-array name="tile_states_cameratoggle">
     <item msgid="6680671247180519913">"ಲಭ್ಯವಿಲ್ಲ"</item>
-    <item msgid="4765607635752003190">"ಆಫ್ ಮಾಡಿ"</item>
+    <item msgid="4765607635752003190">"ಆಫ್"</item>
     <item msgid="1697460731949649844">"ಆನ್ ಮಾಡಿ"</item>
   </string-array>
   <string-array name="tile_states_mictoggle">
     <item msgid="6895831614067195493">"ಲಭ್ಯವಿಲ್ಲ"</item>
-    <item msgid="3296179158646568218">"ಆಫ್ ಮಾಡಿ"</item>
+    <item msgid="3296179158646568218">"ಆಫ್"</item>
     <item msgid="8998632451221157987">"ಆನ್ ಮಾಡಿ"</item>
   </string-array>
   <string-array name="tile_states_controls">
     <item msgid="8199009425335668294">"ಲಭ್ಯವಿಲ್ಲ"</item>
-    <item msgid="4544919905196727508">"ಆಫ್ ಮಾಡಿ"</item>
+    <item msgid="4544919905196727508">"ಆಫ್"</item>
     <item msgid="3422023746567004609">"ಆನ್ ಮಾಡಿ"</item>
   </string-array>
   <string-array name="tile_states_wallet">
     <item msgid="4177615438710836341">"ಲಭ್ಯವಿಲ್ಲ"</item>
-    <item msgid="7571394439974244289">"ಆಫ್ ಮಾಡಿ"</item>
+    <item msgid="7571394439974244289">"ಆಫ್"</item>
     <item msgid="6866424167599381915">"ಆನ್ ಮಾಡಿ"</item>
   </string-array>
   <string-array name="tile_states_qr_code_scanner">
     <item msgid="7435143266149257618">"ಲಭ್ಯವಿಲ್ಲ"</item>
-    <item msgid="3301403109049256043">"ಆಫ್ ಮಾಡಿ"</item>
+    <item msgid="3301403109049256043">"ಆಫ್"</item>
     <item msgid="8878684975184010135">"ಆನ್ ಮಾಡಿ"</item>
   </string-array>
   <string-array name="tile_states_alarm">
     <item msgid="4936533380177298776">"ಲಭ್ಯವಿಲ್ಲ"</item>
-    <item msgid="2710157085538036590">"ಆಫ್ ಮಾಡಿ"</item>
+    <item msgid="2710157085538036590">"ಆಫ್"</item>
     <item msgid="7809470840976856149">"ಆನ್ ಮಾಡಿ"</item>
   </string-array>
   <string-array name="tile_states_onehanded">
     <item msgid="8189342855739930015">"ಲಭ್ಯವಿಲ್ಲ"</item>
-    <item msgid="146088982397753810">"ಆಫ್ ಮಾಡಿ"</item>
+    <item msgid="146088982397753810">"ಆಫ್"</item>
     <item msgid="460891964396502657">"ಆನ್ ಮಾಡಿ"</item>
   </string-array>
   <string-array name="tile_states_dream">
     <item msgid="6184819793571079513">"ಲಭ್ಯವಿಲ್ಲ"</item>
-    <item msgid="8014986104355098744">"ಆಫ್ ಮಾಡಿ"</item>
+    <item msgid="8014986104355098744">"ಆಫ್"</item>
     <item msgid="5966994759929723339">"ಆನ್ ಮಾಡಿ"</item>
   </string-array>
   <string-array name="tile_states_font_scaling">
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 7bd6e6f..897b266 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"설정에서 기본 메모 앱 설정"</string>
     <string name="install_app" msgid="5066668100199613936">"앱 설치"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"외부 디스플레이로 미러링하시겠습니까?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"내부 디스플레이가 미러링됩니다. 전면 디스플레이는 꺼집니다."</string>
     <string name="mirror_display" msgid="2515262008898122928">"디스플레이 미러링"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"닫기"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"디스플레이 연결됨"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index d4974e8..2f091ec 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Параметрлерден демейки кыска жазуулар колдонмосун тууралаңыз"</string>
     <string name="install_app" msgid="5066668100199613936">"Колдонмону орнотуу"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Тышкы экранга чыгарасызбы?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Ички экраныңыз башка экранга чыгарылат. Алдыңкы экраныңыз өчүрүлөт."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Тышкы экран"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Жабуу"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Экран туташтырылды"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 431b77b..c0c5d44 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ຕັ້ງຄ່າແອັບຈົດບັນທຶກເລີ່ມຕົ້ນໃນການຕັ້ງຄ່າ"</string>
     <string name="install_app" msgid="5066668100199613936">"ຕິດຕັ້ງແອັບ"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ສາຍໃສ່ຈໍສະແດງຜົນພາຍນອກບໍ?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"ການສະແດງຜົນທາງໃນຂອງທ່ານຈະຖືກສະທ້ອນ. ການສະແດງຜົນທາງໜ້າຂອງທ່ານຈະຖືກປິດໄວ້."</string>
     <string name="mirror_display" msgid="2515262008898122928">"ຈໍສະແດງຜົນແບບສະທ້ອນ"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"ປິດໄວ້"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"ເຊື່ອມຕໍ່ຈໍແລ້ວ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index a0eaea2..83d2724 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Nustatykite numatytąją užrašų programą Nustatymuose"</string>
     <string name="install_app" msgid="5066668100199613936">"Įdiegti programą"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Bendrinti ekrano vaizdą išoriniame ekrane?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Bus bendrinamas vidinio rodinio ekrano vaizdas. Priekinis rodinys bus išjungtas."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Bendrinti ekrano vaizdą"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Atsisakyti"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Ekranas prijungtas"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index a419f87..4bc71c3 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Iestatījumos iestatiet noklusējuma piezīmju lietotni."</string>
     <string name="install_app" msgid="5066668100199613936">"Instalēt lietotni"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Vai spoguļot ārējā displejā?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Jūsu iekšējais displejs tiks spoguļots. Jūsu priekšējais displejs tiks izslēgts."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Spoguļot displeju"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Nerādīt"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Pievienots displejs"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index ca9fe9e..28a2a02 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Поставете стандардна апликација за белешки во „Поставки“"</string>
     <string name="install_app" msgid="5066668100199613936">"Инсталирајте ја апликацијата"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Да се преслика на надворешниот екран?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Вашиот внатрешен екран ќе се отслика. Вашиот преден екран ќе се исклучи."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Пресликај екран"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Отфрли"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Екранот е поврзан"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 5ff4656..057f0b0 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ക്രമീകരണത്തിൽ കുറിപ്പുകൾക്കുള്ള ഡിഫോൾട്ട് ആപ്പ് സജ്ജീകരിക്കുക"</string>
     <string name="install_app" msgid="5066668100199613936">"ആപ്പ് ഇൻസ്റ്റാൾ ചെയ്യൂ"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ബാഹ്യ ഡിസ്‌പ്ലേയിലേക്ക് മിറർ ചെയ്യണോ?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"നിങ്ങളുടെ ഇന്നർ ഡിസ്പ്ലേ മിറർ ചെയ്യും. നിങ്ങളുടെ ഫ്രണ്ട് ഡിസ്പ്ലേ ഓഫാകും."</string>
     <string name="mirror_display" msgid="2515262008898122928">"മിറർ ഡിസ്‌പ്ലേ"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"ഡിസ്‌മിസ് ചെയ്യുക"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"ഡിസ്പ്ലേ കണക്റ്റ് ചെയ്തിരിക്കുന്നു"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index a71aca2..933652b 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Тохиргоонд тэмдэглэлийн өгөгдмөл апп тохируулна уу"</string>
     <string name="install_app" msgid="5066668100199613936">"Аппыг суулгах"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Гадны дэлгэцэд тусгал үүсгэх үү?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Таны дотоод дэлгэцийн тусгалыг үүсгэнэ. Таны урд талын дэлгэцийг унтраана."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Дэлгэцийн тусгал үүсгэх"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Хаах"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Дэлгэц холбогдсон"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 9dfaa00..aad269b 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"सेटिंग्ज मध्ये डीफॉल्ट टिपा अ‍ॅप सेट करा"</string>
     <string name="install_app" msgid="5066668100199613936">"अ‍ॅप इंस्टॉल करा"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"बाह्य डिस्प्लेवर मिरर करायचे आहे का?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"तुमचा अंतर्गत डिस्प्ले मिरर केला जाईल. तुमचा पुढील डिस्प्ले बंद केला जाईल."</string>
     <string name="mirror_display" msgid="2515262008898122928">"डिस्प्ले मिरर करा"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"डिसमिस करा"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"डिस्प्ले कनेक्ट केला आहे"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 553d221..8287d2b 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Tetapkan apl nota lalai dalam Tetapan"</string>
     <string name="install_app" msgid="5066668100199613936">"Pasang apl"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Paparkan pada paparan luaran?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Paparan dalaman anda akan dicerminkan. Paparan depan anda akan dimatikan."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Segerakkan paparan"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Ketepikan"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Paparan disambungkan"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index d3f1bad..25afad4 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ဆက်တင်များတွင် မူရင်းမှတ်စုများအက်ပ် သတ်မှတ်ပါ"</string>
     <string name="install_app" msgid="5066668100199613936">"အက်ပ် ထည့်သွင်းရန်"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ပြင်ပဖန်သားပြင်သို့ စကရင်ပွားမလား။"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"အတွင်းပြကွက်ကို စကရင်ပွားပါမည်။ ရှေ့မျက်နှာပြင်ပြကွက်ကို ပိတ်မည်။"</string>
     <string name="mirror_display" msgid="2515262008898122928">"ဖန်သားပြင်ကို စကရင်ပွားရန်"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"ပယ်ရန်"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"ဖန်သားပြင်ကို ချိတ်ဆက်လိုက်ပါပြီ"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 234e725..d915743 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Du kan velge en standardapp for notater i Innstillinger"</string>
     <string name="install_app" msgid="5066668100199613936">"Installer appen"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Vil du speile til en ekstern skjerm?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Den indre skjermen speiles. Den ytre skjermen slås av."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Speil skjermen"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Lukk"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"En skjerm er koblet til"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 51839db..4abb4a4 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"सेटिङमा गई नोट बनाउने डिफल्ट एप तोक्नुहोस्"</string>
     <string name="install_app" msgid="5066668100199613936">"एप इन्स्टल गर्नुहोस्"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"बाह्य डिस्प्लेमा मिरर गर्ने हो?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"तपाईंको भित्री डिस्प्ले मिरर गरिने छ। तपाईंको अगाडिको डिस्प्ले अफ गरिने छ।"</string>
     <string name="mirror_display" msgid="2515262008898122928">"डिस्प्ले मिरर गर्नुहोस्"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"खारेज गर्नुहोस्"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"डिस्प्ले कनेक्ट गरिएको छ"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index f66b4e1..0576730 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Standaard notitie-app instellen in Instellingen"</string>
     <string name="install_app" msgid="5066668100199613936">"App installeren"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Spiegelen naar extern scherm?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Het scherm aan de binnenkant wordt gemirrord. Het scherm aan de voorkant wordt uitgezet."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Scherm spiegelen"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Sluiten"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Scherm verbonden"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 0a45769..cb58e64 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ସେଟିଂସରେ ଡିଫଲ୍ଟ ନୋଟ୍ସ ଆପ ସେଟ କରନ୍ତୁ"</string>
     <string name="install_app" msgid="5066668100199613936">"ଆପ ଇନଷ୍ଟଲ କରନ୍ତୁ"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ଏକ୍ସଟର୍ନଲ ଡିସପ୍ଲେକୁ ମିରର କରିବେ?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"ଆପଣଙ୍କ ଇନର ଡିସପ୍ଲେକୁ ମିରର କରାଯିବ। ଆପଣଙ୍କ ଫ୍ରଣ୍ଟ ଡିସପ୍ଲେକୁ ବନ୍ଦ କରାଯିବ।"</string>
     <string name="mirror_display" msgid="2515262008898122928">"ଡିସପ୍ଲେ ମିରର କରନ୍ତୁ"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"ଖାରଜ କରନ୍ତୁ"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"ଡିସପ୍ଲେ କନେକ୍ଟ କରାଯାଇଛି"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index c83659f..0340bd1 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਜਾ ਕੇ ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਨੋਟ ਐਪ ਨੂੰ ਸੈੱਟ ਕਰੋ"</string>
     <string name="install_app" msgid="5066668100199613936">"ਐਪ ਸਥਾਪਤ ਕਰੋ"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ਕੀ ਬਾਹਰੀ ਡਿਸਪਲੇ \'ਤੇ ਪ੍ਰਤਿਬਿੰਬਿਤ ਕਰਨਾ ਹੈ?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"ਤੁਹਾਡੀ ਅੰਦਰੂਨੀ ਡਿਸਪਲੇ ਪ੍ਰਤੀਬਿੰਬਤ ਕੀਤੀ ਜਾਵੇਗੀ। ਤੁਹਾਡੀ ਅਗਲੀ ਡਿਸਪਲੇ ਬੰਦ ਕਰ ਦਿੱਤੀ ਜਾਵੇਗੀ।"</string>
     <string name="mirror_display" msgid="2515262008898122928">"ਡਿਸਪਲੇ ਨੂੰ ਪ੍ਰਤਿਬਿੰਬਿਤ ਕਰੋ"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"ਖਾਰਜ ਕਰੋ"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"ਡਿਸਪਲੇ ਨੂੰ ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 91f2c78..aa24818 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Ustaw domyślną aplikację do obsługi notatek w Ustawieniach"</string>
     <string name="install_app" msgid="5066668100199613936">"Zainstaluj aplikację"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Powielić na wyświetlaczu zewnętrznym?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Powstanie odbicie lustrzane Twojego wewnętrznego ekranu. Przedni ekran zostanie wyłączony."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Powielaj obraz"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Zamknij"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Wyświetlacz podłączony"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 9fe372b..c65c56e 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Defina o app de notas padrão nas Configurações"</string>
     <string name="install_app" msgid="5066668100199613936">"Instalar o app"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Espelhar para a tela externa?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Seu display interno será espelhado. O display frontal será desligado."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Espelhar tela"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Dispensar"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Tela conectada"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 3bf9c7b..0b9580d 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Predefina a app de notas nas Definições"</string>
     <string name="install_app" msgid="5066668100199613936">"Instalar app"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Espelhar para o ecrã externo?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"O seu ecrã interior vai ser espelhado. O seu ecrã frontal vai ser desativado."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Espelhar ecrã"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Ignorar"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Ecrã ligado"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 9fe372b..c65c56e 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Defina o app de notas padrão nas Configurações"</string>
     <string name="install_app" msgid="5066668100199613936">"Instalar o app"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Espelhar para a tela externa?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Seu display interno será espelhado. O display frontal será desligado."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Espelhar tela"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Dispensar"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Tela conectada"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 18aa668..b2fdef3 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Setează aplicația prestabilită de note din Setări"</string>
     <string name="install_app" msgid="5066668100199613936">"Instalează aplicația"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Oglindești pe ecranul extern?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Ecranul interior va fi oglindit. Ecranul frontal va fi dezactivat."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Afișare în oglindă"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Închide"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Ecran conectat"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 51192f8..ff4bd45 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Задайте стандартное приложение для заметок в настройках."</string>
     <string name="install_app" msgid="5066668100199613936">"Установить приложение"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Дублировать на внешний дисплей?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Для внутреннего экрана включится дублирование. Передний экран будет отключен."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Дублировать"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Закрыть"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Экран подключен"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 7ade110..4b4d08b 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"සැකසීම් තුළ පෙරනිමි සටහන් යෙදුම සකසන්න"</string>
     <string name="install_app" msgid="5066668100199613936">"යෙදුම ස්ථාපනය කරන්න"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"බාහිර සංදර්ශකයට දර්පණය කරන්න ද?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"ඔබේ අභ්‍යන්තර සංදර්ශකය පිළිබිඹු වනු ඇත. ඔබේ ඉදිරිපස සංදර්ශකය ක්‍රියාවිරහිත වනු ඇත."</string>
     <string name="mirror_display" msgid="2515262008898122928">"සංදර්ශකය දර්පණය කරන්න"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"අස් කරන්න"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"සංදර්ශකය සම්බන්ධ කර ඇත"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 8d760d5..9e9507e 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Nastavte predvolenú aplikáciu na poznámky v Nastaveniach"</string>
     <string name="install_app" msgid="5066668100199613936">"Inštalovať aplikáciu"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Chcete zrkadliť na externú obrazovku?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Vnútorná obrazovka bude zrkadlená. Predná obrazovka bude vypnutá."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Zrkadliť obrazovku"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Zavrieť"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Obrazovka je pripojená"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 4ae80ef..cba5416 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Nastavite privzeto aplikacijo za zapiske v nastavitvah."</string>
     <string name="install_app" msgid="5066668100199613936">"Namesti aplikacijo"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Želite zrcaliti na zunanji zaslon?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Notranji zaslon bo zrcaljen. Sprednji zaslon bo izklopljen."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Zrcali zaslon"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Opusti"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Zaslon je povezan"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 8372703..b35668f 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Cakto aplikacionin e parazgjedhur të shënimeve te \"Cilësimet\""</string>
     <string name="install_app" msgid="5066668100199613936">"Instalo aplikacionin"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Të pasqyrohet në ekranin e jashtëm?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Ekrani i brendshëm do të pasqyrohet. Ekrani i parmë do të çaktivizohet."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Pasqyro ekranin"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Hiq"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Ekrani është lidhur"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 1bda1db..1d45fbd 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Подесите подразумевану апликацију за белешке у Подешавањима"</string>
     <string name="install_app" msgid="5066668100199613936">"Инсталирај апликацију"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Желите ли да пресликате на спољњи екран?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Унутрашњи екран ће се пресликати. Предњи екран ће се искључити."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Пресликај екран"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Одбаци"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Екран је повезан"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 8cfafcf..0d6272f 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Ställ in en standardapp för anteckningar i inställningarna"</string>
     <string name="install_app" msgid="5066668100199613936">"Installera appen"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Vill du spegla till extern skärm?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Den inre skärmen speglas. Den främre skärmen stängs av."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Spegla skärm"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Ignorera"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Skärm har anslutits"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 286e65a..1bd2ed7 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -252,7 +252,7 @@
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Hakuna vifaa vilivyooanishwa vinavyopatikana"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Gusa ili uunganishe au utenganishe kifaa"</string>
-    <string name="pair_new_bluetooth_devices" msgid="4601767620843349645">"Oanisha kifaa kipya"</string>
+    <string name="pair_new_bluetooth_devices" msgid="4601767620843349645">"Unganisha kifaa kipya"</string>
     <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>
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Teua programu chaguomsingi ya madokezo katika Mipangilio"</string>
     <string name="install_app" msgid="5066668100199613936">"Sakinisha programu"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Ungependa kuonyesha kwenye skrini ya nje?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Mwonekano wa ndani wa kifaa chako utaakisiwa. Mwonekano wa mbele wa kifaa chako utazimwa."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Akisi skrini"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Ondoa"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Skrini imeunganishwa"</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index aab713f..0cd076f 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"குறிப்பு எடுப்பதற்கான இயல்புநிலை ஆப்ஸை அமைப்புகளில் அமையுங்கள்"</string>
     <string name="install_app" msgid="5066668100199613936">"ஆப்ஸை நிறுவுங்கள்"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"வெளிப்புறக் காட்சிக்கு மிரர் செய்யவா?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"உங்கள் உட்புற டிஸ்பிளே பிரதிபலிக்கப்படும். உங்கள் முன்புற டிஸ்பிளே முடக்கப்படும்."</string>
     <string name="mirror_display" msgid="2515262008898122928">"டிஸ்பிளேயை மிரர் செய்"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"வேண்டாம்"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"டிஸ்ப்ளே இணைக்கப்பட்டது"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 2536a96..6a59812 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"సెట్టింగ్‌లలో ఆటోమేటిక్‌గా ఉండేలా ఒక నోట్స్ యాప్‌ను సెట్ చేసుకోండి"</string>
     <string name="install_app" msgid="5066668100199613936">"యాప్‌ను ఇన్‌స్టాల్ చేయండి"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"ఎక్స్‌టర్నల్ డిస్‌ప్లే‌కి మిర్రర్‌ చేయాలా?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"మీ లోపలి డిస్‌ప్లే మిర్రర్ చేయబడుతుంది. మీ ముందు వైపు డిస్‌ప్లే ఆఫ్ చేయబడుతుంది."</string>
     <string name="mirror_display" msgid="2515262008898122928">"మిర్రర్ డిస్‌ప్లే"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"విస్మరించండి"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"డిస్‌ప్లే కనెక్ట్ చేయబడింది"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 3c96e30..dc4c6cf 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"กำหนดแอปการจดบันทึกเริ่มต้นในการตั้งค่า"</string>
     <string name="install_app" msgid="5066668100199613936">"ติดตั้งแอป"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"มิเรอร์ไปยังจอแสดงผลภายนอกไหม"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"ระบบจะมิเรอร์หน้าจอด้านใน และจะปิดหน้าจอด้านหน้า"</string>
     <string name="mirror_display" msgid="2515262008898122928">"มิเรอร์จอแสดงผล"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"ปิด"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"เชื่อมต่อจอแสดงผลแล้ว"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 8fc5b6b..c09ac97 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Magtakda ng default na app sa pagtatala sa Mga Setting"</string>
     <string name="install_app" msgid="5066668100199613936">"I-install ang app"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"I-mirror sa external na display?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Imi-mirror ang inner display mo. Io-off ang iyong front display."</string>
     <string name="mirror_display" msgid="2515262008898122928">"I-mirror ang display"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"I-dismiss"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Naikonekta ang display"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index a4083ef..ee1909b 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Ayarlar\'ı kullanarak varsayılan notlar ayarlayın"</string>
     <string name="install_app" msgid="5066668100199613936">"Uygulamayı yükle"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Harici ekrana yansıtılsın mı?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"İç ekranınız yansıtılacak. Ön ekranınız kapatılacak."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Ekranı yansıt"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Kapat"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Ekran bağlandı"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 5e0f2c2..04a97bc 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Призначте стандартний додаток для нотаток у налаштуваннях"</string>
     <string name="install_app" msgid="5066668100199613936">"Установити додаток"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Дублювати на зовнішньому екрані?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Ваш внутрішній екран буде продубльовано. Передній екран буде вимкнено."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Дублювати екран"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Закрити"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Дисплей під’єднано"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index c28e064..fd984b9 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"ترتیبات میں ڈیفالٹ نوٹس ایپ سیٹ کریں"</string>
     <string name="install_app" msgid="5066668100199613936">"ایپ انسٹال کریں"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"بیرونی ڈسپلے پر مرر کریں؟"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"آپ کے اندرونی ڈسپلے کو دو طرفہ مطابقت پذیر بنایا جائے گا۔ آپ کا فرنٹ ڈسپلے آف ہو جائے گا۔"</string>
     <string name="mirror_display" msgid="2515262008898122928">"ڈسپلے کو مرر کریں"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"برخاست کریں"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"ڈسپلے منسلک ہے"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index e032c76..b9a9832 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Standart qaydlar ilovasini Sozlamalar orqali tanlang"</string>
     <string name="install_app" msgid="5066668100199613936">"Ilovani oʻrnatish"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Tashqi displeyda aks ettirilsinmi?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Ichki ekran uchun aks ettirish yoqiladi. Old ekran oʻchiriladi."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Displeyni aks ettirish"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Yopish"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Displey ulandi"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index b87619e..b1ff9a8 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Đặt ứng dụng ghi chú mặc định trong phần Cài đặt"</string>
     <string name="install_app" msgid="5066668100199613936">"Cài đặt ứng dụng"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Phản chiếu sang màn hình ngoài?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Màn hình trong của bạn sẽ được phản chiếu. Màn hình ngoài của bạn sẽ tắt."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Phản chiếu màn hình"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Đóng"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Đã kết nối 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 43dfbe8..237fd57 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"在设置中设置默认记事应用"</string>
     <string name="install_app" msgid="5066668100199613936">"安装应用"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"镜像到外接显示屏?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"系统将镜像您的内屏,而关闭外屏。"</string>
     <string name="mirror_display" msgid="2515262008898122928">"镜像到显示屏"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"关闭"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"显示屏已连接"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 784f667..313af30 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"在「設定」中指定預設筆記應用程式"</string>
     <string name="install_app" msgid="5066668100199613936">"安裝應用程式"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"要鏡像投射至外部顯示屏嗎?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"鏡像畫面將顯示在內部螢幕,前方螢幕則會關閉。"</string>
     <string name="mirror_display" msgid="2515262008898122928">"鏡像顯示"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"關閉"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"已連接顯示屏"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 15da239..6a13d3d 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"在「設定」中指定預設記事應用程式"</string>
     <string name="install_app" msgid="5066668100199613936">"安裝應用程式"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"要以鏡像方式投放至外部螢幕嗎?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"鏡像畫面將顯示在內螢幕,封面螢幕則會關閉。"</string>
     <string name="mirror_display" msgid="2515262008898122928">"鏡像顯示"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"關閉"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"螢幕已連結"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 31cdca9..23862a7 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -1208,8 +1208,7 @@
     <string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Setha i-app yamanothi azenzakalelayo Kumsethingi"</string>
     <string name="install_app" msgid="5066668100199613936">"Faka i-app"</string>
     <string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Fanisa nesibonisi sangaphandle?"</string>
-    <!-- no translation found for connected_display_dialog_dual_display_stop_warning (4174707498892447947) -->
-    <skip />
+    <string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Isibonisi sakho sangaphakathi sizoboniswa. Isibonisi sakho sangaphambili sizovalwa."</string>
     <string name="mirror_display" msgid="2515262008898122928">"Isibonisi sokufanisa"</string>
     <string name="dismiss_dialog" msgid="2195508495854675882">"Chitha"</string>
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Isibonisi sixhunyiwe"</string>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 5c362b2..10f7c4d 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -962,6 +962,9 @@
     <!-- Whether to show bottom sheets edge to edge -->
     <bool name="config_edgeToEdgeBottomSheetDialog">true</bool>
 
+    <!-- Device specific config that controls whether rest to unlock feature is supported.  -->
+    <bool name="config_restToUnlockSupported">false</bool>
+
     <!--
     Time in milliseconds the user has to touch the side FPS sensor to successfully authenticate when
     the screen is turned off with AOD not enabled.
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index ee89ede..90d8cdb 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -608,6 +608,11 @@
     <dimen name="volume_panel_slice_vertical_padding">8dp</dimen>
     <dimen name="volume_panel_slice_horizontal_padding">24dp</dimen>
 
+    <dimen name="volume_panel_corner_radius">52dp</dimen>
+    <dimen name="volume_panel_content_padding">24dp</dimen>
+    <dimen name="volume_panel_bottom_bar_horizontal_padding">24dp</dimen>
+    <dimen name="volume_panel_bottom_bar_bottom_padding">20dp</dimen>
+
     <!-- Size of each item in the ringer selector drawer. -->
     <dimen name="volume_ringer_drawer_item_size">42dp</dimen>
     <dimen name="volume_ringer_drawer_item_size_half">21dp</dimen>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index c48dd9d..3f026a4 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -930,6 +930,15 @@
         <item name="wallpaperTextColor">@*android:color/primary_text_material_dark</item>
     </style>
 
+    <style name="Theme.VolumePanelActivity" parent="@style/Theme.SystemUI">
+        <item name="android:windowIsTranslucent">true</item>
+        <item name="android:windowBackground">@android:color/transparent</item>
+        <item name="android:windowActionBar">false</item>
+        <item name="android:windowNoTitle">true</item>
+        <!-- Setting a placeholder will avoid using the SystemUI icon on the splash screen.  -->
+        <item name="android:windowSplashScreenAnimatedIcon">@drawable/ic_blank</item>
+    </style>
+
     <style name="Theme.UserSwitcherFullscreenDialog" parent="@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen">
         <item name="android:statusBarColor">@color/user_switcher_fullscreen_bg</item>
         <item name="android:windowBackground">@color/user_switcher_fullscreen_bg</item>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index f3cd01f..4d84d0b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -217,7 +217,8 @@
     private static final int MSG_BIOMETRIC_ENROLLMENT_STATE_CHANGED = 348;
 
     /** Biometric authentication state: Not listening. */
-    private static final int BIOMETRIC_STATE_STOPPED = 0;
+    @VisibleForTesting
+    protected static final int BIOMETRIC_STATE_STOPPED = 0;
 
     /** Biometric authentication state: Listening. */
     private static final int BIOMETRIC_STATE_RUNNING = 1;
@@ -1803,6 +1804,7 @@
                 public void onFingerprintDetected(int sensorId, int userId,
                         boolean isStrongBiometric) {
                     handleBiometricDetected(userId, FINGERPRINT, isStrongBiometric);
+                    setFingerprintRunningState(BIOMETRIC_STATE_STOPPED);
                 }
             };
 
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 175fcdb..d5dc85c 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -26,7 +26,6 @@
 import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
 import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1;
 import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED;
-import static com.android.systemui.Flags.newAodTransition;
 import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
 
 import android.annotation.SuppressLint;
@@ -395,16 +394,6 @@
             mView.updateIcon(ICON_LOCK, true);
             mView.setContentDescription(mLockedLabel);
             mView.setVisibility(View.VISIBLE);
-        } else if (mIsDozing && newAodTransition()) {
-            mView.animate()
-                    .alpha(0f)
-                    .setDuration(FADE_OUT_DURATION_MS)
-                    .withEndAction(() -> {
-                        mView.clearIcon();
-                        mView.setVisibility(View.INVISIBLE);
-                        mView.setContentDescription(null);
-                    })
-                    .start();
         } else {
             mView.clearIcon();
             mView.setVisibility(View.INVISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt
index c320350..348b54e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt
@@ -53,6 +53,9 @@
     private val logger: SideFpsLogger,
 ) {
 
+    private val isProlongedTouchEnabledForDevice =
+        context.resources.getBoolean(R.bool.config_restToUnlockSupported)
+
     private val sensorLocationForCurrentDisplay =
         combine(
                 displayStateInteractor.displayChanges,
@@ -82,7 +85,7 @@
             .onEach { logger.authDurationChanged(it) }
 
     val isProlongedTouchRequiredForAuthentication: Flow<Boolean> =
-        if (fingerprintInteractiveToAuthProvider.isEmpty) {
+        if (fingerprintInteractiveToAuthProvider.isEmpty || !isProlongedTouchEnabledForDevice) {
             flowOf(false)
         } else {
             combine(
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index 90e4a38..7b8cb82 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -77,6 +77,13 @@
         applicationScope: CoroutineScope,
         vibratorHelper: VibratorHelper,
     ): Spaghetti {
+        /**
+         * View is only set visible in BiometricViewSizeBinder once PromptSize is determined that
+         * accounts for iconView size, to prevent prompt resizing being visible to the user.
+         *
+         * TODO(b/288175072): May be able to remove this once constraint layout is implemented
+         */
+        view.visibility = View.INVISIBLE
         val accessibilityManager = view.context.getSystemService(AccessibilityManager::class.java)!!
 
         val textColorError =
@@ -102,7 +109,7 @@
             iconView,
             iconOverlayView,
             view.getUpdatedFingerprintAffordanceSize(),
-            viewModel.iconViewModel
+            viewModel
         )
 
         val indicatorMessageView = view.requireViewById<TextView>(R.id.indicator)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
index 7e16d1e..1a7b6c9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
@@ -30,7 +30,6 @@
 import androidx.core.view.doOnLayout
 import androidx.core.view.isGone
 import androidx.lifecycle.lifecycleScope
-import com.android.systemui.res.R
 import com.android.systemui.biometrics.AuthPanelController
 import com.android.systemui.biometrics.Utils
 import com.android.systemui.biometrics.ui.BiometricPromptLayout
@@ -41,6 +40,8 @@
 import com.android.systemui.biometrics.ui.viewmodel.isNullOrNotSmall
 import com.android.systemui.biometrics.ui.viewmodel.isSmall
 import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.res.R
+import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.launch
 
 /** Helper for [BiometricViewBinder] to handle resize transitions. */
@@ -93,7 +94,20 @@
             view.repeatWhenAttached {
                 var currentSize: PromptSize? = null
                 lifecycleScope.launch {
-                    viewModel.size.collect { size ->
+                    /**
+                     * View is only set visible in BiometricViewSizeBinder once PromptSize is
+                     * determined that accounts for iconView size, to prevent prompt resizing being
+                     * visible to the user.
+                     *
+                     * TODO(b/288175072): May be able to remove isIconViewLoaded once constraint
+                     *   layout is implemented
+                     */
+                    combine(viewModel.isIconViewLoaded, viewModel.size, ::Pair).collect {
+                        (isIconViewLoaded, size) ->
+                        if (!isIconViewLoaded) {
+                            return@collect
+                        }
+
                         // prepare for animated size transitions
                         for (v in viewsToHideWhenSmall) {
                             v.showTextOrHide(forceHide = size.isSmall)
@@ -198,6 +212,8 @@
                             }
 
                             currentSize = size
+                            view.visibility = View.VISIBLE
+                            viewModel.setIsIconViewLoaded(false)
                             notifyAccessibilityChanged()
                         }
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt
index 475ef18..6e3bcf5 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt
@@ -26,6 +26,7 @@
 import com.android.settingslib.widget.LottieColorUtils
 import com.android.systemui.biometrics.ui.viewmodel.PromptIconViewModel
 import com.android.systemui.biometrics.ui.viewmodel.PromptIconViewModel.AuthType
+import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.util.kotlin.Utils.Companion.toQuad
 import com.android.systemui.util.kotlin.Utils.Companion.toQuint
@@ -45,8 +46,9 @@
         iconView: LottieAnimationView,
         iconOverlayView: LottieAnimationView,
         iconViewLayoutParamSizeOverride: Pair<Int, Int>?,
-        viewModel: PromptIconViewModel
+        promptViewModel: PromptViewModel
     ) {
+        val viewModel = promptViewModel.iconViewModel
         iconView.repeatWhenAttached {
             repeatOnLifecycle(Lifecycle.State.STARTED) {
                 viewModel.onConfigurationChanged(iconView.context.resources.configuration)
@@ -71,25 +73,45 @@
                     }
 
                 launch {
+                    var width: Int
+                    var height: Int
                     viewModel.activeAuthType.collect { activeAuthType ->
-                        if (iconViewLayoutParamSizeOverride == null) {
-                            val width: Int
-                            val height: Int
-                            when (activeAuthType) {
-                                AuthType.Fingerprint,
-                                AuthType.Coex -> {
-                                    width = viewModel.fingerprintIconWidth
-                                    height = viewModel.fingerprintIconHeight
-                                }
-                                AuthType.Face -> {
-                                    width = viewModel.faceIconWidth
-                                    height = viewModel.faceIconHeight
+                        when (activeAuthType) {
+                            AuthType.Fingerprint,
+                            AuthType.Coex -> {
+                                width = viewModel.fingerprintIconWidth
+                                height = viewModel.fingerprintIconHeight
+
+                                /**
+                                 * View is only set visible in BiometricViewSizeBinder once
+                                 * PromptSize is determined that accounts for iconView size, to
+                                 * prevent prompt resizing being visible to the user.
+                                 *
+                                 * TODO(b/288175072): May be able to remove this once constraint
+                                 *   layout is implemented
+                                 */
+                                iconView.removeAllLottieOnCompositionLoadedListener()
+                                iconView.addLottieOnCompositionLoadedListener {
+                                    promptViewModel.setIsIconViewLoaded(true)
                                 }
                             }
+                            AuthType.Face -> {
+                                width = viewModel.faceIconWidth
+                                height = viewModel.faceIconHeight
+                                /**
+                                 * Set to true by default since face icon is a drawable, which
+                                 * doesn't have a LottieOnCompositionLoadedListener equivalent.
+                                 *
+                                 * TODO(b/318569643): To be updated once face assets are updated
+                                 *   from drawables
+                                 */
+                                promptViewModel.setIsIconViewLoaded(true)
+                            }
+                        }
 
+                        if (iconViewLayoutParamSizeOverride == null) {
                             iconView.layoutParams.width = width
                             iconView.layoutParams.height = height
-
                             iconOverlayView.layoutParams.width = width
                             iconOverlayView.layoutParams.height = height
                         }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index 6d0a58e..d899827e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -192,6 +192,28 @@
     val iconViewModel: PromptIconViewModel =
         PromptIconViewModel(this, displayStateInteractor, promptSelectorInteractor)
 
+    private val _isIconViewLoaded = MutableStateFlow(false)
+
+    /**
+     * For prompts with an iconView, false until the prompt's iconView animation has been loaded in
+     * the view, otherwise true by default. Used for BiometricViewSizeBinder to wait for the icon
+     * asset to be loaded before determining the prompt size.
+     */
+    val isIconViewLoaded: Flow<Boolean> =
+        combine(credentialKind, _isIconViewLoaded.asStateFlow()) { credentialKind, isIconViewLoaded
+            ->
+            if (credentialKind is PromptKind.Biometric) {
+                isIconViewLoaded
+            } else {
+                true
+            }
+        }
+
+    // Sets whether the prompt's iconView animation has been loaded in the view yet.
+    fun setIsIconViewLoaded(iconViewLoaded: Boolean) {
+        _isIconViewLoaded.value = iconViewLoaded
+    }
+
     /** Padding for prompt UI elements */
     val promptPadding: Flow<Rect> =
         combine(size, displayStateInteractor.currentRotation) { size, rotation ->
diff --git a/packages/SystemUI/src/com/android/systemui/common/data/CommonDataLayerModule.kt b/packages/SystemUI/src/com/android/systemui/common/data/CommonDataLayerModule.kt
index 27c9b3f..d1c728c 100644
--- a/packages/SystemUI/src/com/android/systemui/common/data/CommonDataLayerModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/data/CommonDataLayerModule.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.common.data
 
+import com.android.systemui.common.data.repository.PackageChangeRepository
+import com.android.systemui.common.data.repository.PackageChangeRepositoryImpl
 import com.android.systemui.common.ui.data.repository.ConfigurationRepository
 import com.android.systemui.common.ui.data.repository.ConfigurationRepositoryImpl
 import dagger.Binds
@@ -23,5 +25,13 @@
 
 @Module
 abstract class CommonDataLayerModule {
-    @Binds abstract fun bindRepository(impl: ConfigurationRepositoryImpl): ConfigurationRepository
+    @Binds
+    abstract fun bindConfigurationRepository(
+        impl: ConfigurationRepositoryImpl
+    ): ConfigurationRepository
+
+    @Binds
+    abstract fun bindPackageChangeRepository(
+        impl: PackageChangeRepositoryImpl
+    ): PackageChangeRepository
 }
diff --git a/packages/SystemUI/src/com/android/systemui/common/data/repository/PackageChangeRepository.kt b/packages/SystemUI/src/com/android/systemui/common/data/repository/PackageChangeRepository.kt
new file mode 100644
index 0000000..7c7b3db
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/data/repository/PackageChangeRepository.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.common.data.repository
+
+import android.os.UserHandle
+import com.android.systemui.common.data.shared.model.PackageChangeModel
+import kotlinx.coroutines.flow.Flow
+
+interface PackageChangeRepository {
+    /**
+     * Emits values when packages for the specified user are changed. See supported modifications in
+     * [PackageChangeModel]
+     *
+     * [UserHandle.USER_ALL] may be used to listen to all users.
+     */
+    fun packageChanged(user: UserHandle): Flow<PackageChangeModel>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/common/data/repository/PackageChangeRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/common/data/repository/PackageChangeRepositoryImpl.kt
new file mode 100644
index 0000000..b1b348c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/data/repository/PackageChangeRepositoryImpl.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.common.data.repository
+
+import android.os.UserHandle
+import com.android.systemui.common.data.shared.model.PackageChangeModel
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
+
+@SysUISingleton
+class PackageChangeRepositoryImpl
+@Inject
+constructor(
+    private val monitorFactory: PackageUpdateMonitor.Factory,
+) : PackageChangeRepository {
+    /**
+     * A [PackageUpdateMonitor] which monitors package updates for all users. The per-user filtering
+     * is done by [packageChanged].
+     */
+    private val monitor by lazy { monitorFactory.create(UserHandle.ALL) }
+
+    override fun packageChanged(user: UserHandle): Flow<PackageChangeModel> =
+        monitor.packageChanged.filter {
+            user == UserHandle.ALL || user == UserHandle.getUserHandleForUid(it.packageUid)
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/common/data/repository/PackageUpdateLogger.kt b/packages/SystemUI/src/com/android/systemui/common/data/repository/PackageUpdateLogger.kt
new file mode 100644
index 0000000..adbb37c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/data/repository/PackageUpdateLogger.kt
@@ -0,0 +1,57 @@
+/*
+ * 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.common.data.repository
+
+import android.os.UserHandle
+import com.android.systemui.common.data.shared.model.PackageChangeModel
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.dagger.PackageChangeRepoLog
+import javax.inject.Inject
+
+private fun getChangeString(model: PackageChangeModel) =
+    when (model) {
+        is PackageChangeModel.Installed -> "installed"
+        is PackageChangeModel.Uninstalled -> "uninstalled"
+        is PackageChangeModel.UpdateStarted -> "started updating"
+        is PackageChangeModel.UpdateFinished -> "finished updating"
+        is PackageChangeModel.Changed -> "changed"
+    }
+
+/** A debug logger for [PackageChangeRepository]. */
+@SysUISingleton
+class PackageUpdateLogger @Inject constructor(@PackageChangeRepoLog private val buffer: LogBuffer) {
+
+    fun logChange(model: PackageChangeModel) {
+        buffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            {
+                str1 = model.packageName
+                str2 = getChangeString(model)
+                int1 = model.packageUid
+            },
+            {
+                val user = UserHandle.getUserHandleForUid(int1)
+                "Package $str1 ($int1) $str2 on user $user"
+            }
+        )
+    }
+}
+
+private const val TAG = "PackageChangeRepoLog"
diff --git a/packages/SystemUI/src/com/android/systemui/common/data/repository/PackageUpdateMonitor.kt b/packages/SystemUI/src/com/android/systemui/common/data/repository/PackageUpdateMonitor.kt
new file mode 100644
index 0000000..f7cc344
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/data/repository/PackageUpdateMonitor.kt
@@ -0,0 +1,127 @@
+/*
+ * 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.common.data.repository
+
+import android.content.Context
+import android.os.Handler
+import android.os.UserHandle
+import com.android.internal.content.PackageMonitor
+import com.android.systemui.common.data.shared.model.PackageChangeModel
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
+
+/**
+ * A wrapper around [PackageMonitor] which exposes package updates as a flow.
+ *
+ * External clients should use [PackageChangeRepository] instead to ensure only a single callback is
+ * registered for all of SystemUI.
+ */
+class PackageUpdateMonitor
+@AssistedInject
+constructor(
+    @Assisted private val user: UserHandle,
+    @Background private val bgDispatcher: CoroutineDispatcher,
+    @Background private val bgHandler: Handler,
+    @Application private val context: Context,
+    @Application private val scope: CoroutineScope,
+    private val logger: PackageUpdateLogger,
+) : PackageMonitor() {
+
+    @AssistedFactory
+    fun interface Factory {
+        fun create(user: UserHandle): PackageUpdateMonitor
+    }
+
+    var isActive = false
+        private set
+
+    private val _packageChanged =
+        MutableSharedFlow<PackageChangeModel>(replay = 0, extraBufferCapacity = BUFFER_CAPACITY)
+            .apply {
+                // Automatically register/unregister as needed, depending on whether
+                // there are subscribers to this flow.
+                subscriptionCount
+                    .map { it > 0 }
+                    .distinctUntilChanged()
+                    .onEach { active ->
+                        if (active) {
+                            register(context, user, bgHandler)
+                        } else if (isActive) {
+                            // Avoid calling unregister if we were not previously active, as this
+                            // will cause an IllegalStateException.
+                            unregister()
+                        }
+                        isActive = active
+                    }
+                    .flowOn(bgDispatcher)
+                    .launchIn(scope)
+            }
+
+    val packageChanged: Flow<PackageChangeModel>
+        get() = _packageChanged.onEach(logger::logChange)
+
+    override fun onPackageAdded(packageName: String, uid: Int) {
+        super.onPackageAdded(packageName, uid)
+        _packageChanged.tryEmit(PackageChangeModel.Installed(packageName, uid))
+    }
+
+    override fun onPackageRemoved(packageName: String, uid: Int) {
+        super.onPackageRemoved(packageName, uid)
+        _packageChanged.tryEmit(PackageChangeModel.Uninstalled(packageName, uid))
+    }
+
+    override fun onPackageChanged(
+        packageName: String,
+        uid: Int,
+        components: Array<out String>
+    ): Boolean {
+        super.onPackageChanged(packageName, uid, components)
+        _packageChanged.tryEmit(PackageChangeModel.Changed(packageName, uid))
+        return false
+    }
+
+    override fun onPackageUpdateStarted(packageName: String, uid: Int) {
+        super.onPackageUpdateStarted(packageName, uid)
+        _packageChanged.tryEmit(PackageChangeModel.UpdateStarted(packageName, uid))
+    }
+
+    override fun onPackageUpdateFinished(packageName: String, uid: Int) {
+        super.onPackageUpdateFinished(packageName, uid)
+        _packageChanged.tryEmit(PackageChangeModel.UpdateFinished(packageName, uid))
+    }
+
+    private companion object {
+        // This capacity is the number of package changes that we will keep buffered in the shared
+        // flow. It is unlikely that at any given time there would be this many changes being
+        // processed by consumers, but this is done just in case that many packages are changed at
+        // the same time and there is backflow due to consumers processing the changes more slowly
+        // than they are being emitted.
+        const val BUFFER_CAPACITY = 100
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/common/data/shared/model/PackageChangeModel.kt b/packages/SystemUI/src/com/android/systemui/common/data/shared/model/PackageChangeModel.kt
new file mode 100644
index 0000000..853eff7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/data/shared/model/PackageChangeModel.kt
@@ -0,0 +1,71 @@
+/*
+ * 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.common.data.shared.model
+
+import android.content.Intent
+
+/** Represents changes to an installed package. */
+sealed interface PackageChangeModel {
+    val packageName: String
+    val packageUid: Int
+
+    /**
+     * An existing application package was uninstalled.
+     *
+     * Equivalent to receiving the [Intent.ACTION_PACKAGE_REMOVED] broadcast with
+     * [Intent.EXTRA_REPLACING] set to false.
+     */
+    data class Uninstalled(override val packageName: String, override val packageUid: Int) :
+        PackageChangeModel
+
+    /**
+     * A new version of an existing application is going to be installed.
+     *
+     * Equivalent to receiving the [Intent.ACTION_PACKAGE_REMOVED] broadcast with
+     * [Intent.EXTRA_REPLACING] set to true.
+     */
+    data class UpdateStarted(override val packageName: String, override val packageUid: Int) :
+        PackageChangeModel
+
+    /**
+     * A new version of an existing application package has been installed, replacing the old
+     * version.
+     *
+     * Equivalent to receiving the [Intent.ACTION_PACKAGE_ADDED] broadcast with
+     * [Intent.EXTRA_REPLACING] set to true.
+     */
+    data class UpdateFinished(override val packageName: String, override val packageUid: Int) :
+        PackageChangeModel
+
+    /**
+     * A new application package has been installed.
+     *
+     * Equivalent to receiving the [Intent.ACTION_PACKAGE_ADDED] broadcast with
+     * [Intent.EXTRA_REPLACING] set to false.
+     */
+    data class Installed(override val packageName: String, override val packageUid: Int) :
+        PackageChangeModel
+
+    /**
+     * An existing application package has been changed (for example, a component has been enabled
+     * or disabled).
+     *
+     * Equivalent to receiving the [Intent.ACTION_PACKAGE_CHANGED] broadcast.
+     */
+    data class Changed(override val packageName: String, override val packageUid: Int) :
+        PackageChangeModel
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
index 847b98e..10768ea 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.communal.dagger
 
-import android.content.Context
 import com.android.systemui.communal.data.db.CommunalDatabaseModule
 import com.android.systemui.communal.data.repository.CommunalMediaRepositoryModule
 import com.android.systemui.communal.data.repository.CommunalRepositoryModule
@@ -24,9 +23,8 @@
 import com.android.systemui.communal.data.repository.CommunalWidgetRepositoryModule
 import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
 import com.android.systemui.communal.widgets.EditWidgetsActivityStarterImpl
-import com.android.systemui.dagger.qualifiers.Application
+import dagger.Binds
 import dagger.Module
-import dagger.Provides
 
 @Module(
     includes =
@@ -38,11 +36,9 @@
             CommunalDatabaseModule::class,
         ]
 )
-class CommunalModule {
-    @Provides
-    fun provideEditWidgetsActivityStarter(
-        @Application context: Context
-    ): EditWidgetsActivityStarter {
-        return EditWidgetsActivityStarterImpl(context)
-    }
+interface CommunalModule {
+    @Binds
+    fun bindEditWidgetsActivityStarter(
+        starter: EditWidgetsActivityStarterImpl
+    ): EditWidgetsActivityStarter
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
index d1bbe57..cab8adf 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
@@ -38,6 +38,7 @@
 import com.android.systemui.log.core.Logger
 import com.android.systemui.log.dagger.CommunalLog
 import com.android.systemui.settings.UserTracker
+import java.util.Optional
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
@@ -75,7 +76,7 @@
 class CommunalWidgetRepositoryImpl
 @Inject
 constructor(
-    private val appWidgetManager: AppWidgetManager,
+    private val appWidgetManager: Optional<AppWidgetManager>,
     private val appWidgetHost: AppWidgetHost,
     @Application private val applicationScope: CoroutineScope,
     @Background private val bgDispatcher: CoroutineDispatcher,
@@ -144,7 +145,7 @@
 
     override val communalWidgets: Flow<List<CommunalWidgetContentModel>> =
         isHostActive.flatMapLatest { isHostActive ->
-            if (!isHostActive) {
+            if (!isHostActive || !appWidgetManager.isPresent) {
                 return@flatMapLatest flowOf(emptyList())
             }
             communalWidgetDao.getWidgets().map { it.map(::mapToContentModel) }
@@ -187,7 +188,7 @@
         val (_, widgetId) = entry.value
         return CommunalWidgetContentModel(
             appWidgetId = widgetId,
-            providerInfo = appWidgetManager.getAppWidgetInfo(widgetId),
+            providerInfo = appWidgetManager.get().getAppWidgetInfo(widgetId),
             priority = entry.key.rank,
         )
     }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt
index 5793f10..d0d9e3f 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt
@@ -31,6 +31,7 @@
 import dagger.Binds
 import dagger.Module
 import dagger.Provides
+import java.util.Optional
 import javax.inject.Named
 
 @Module
@@ -41,8 +42,8 @@
 
         @SysUISingleton
         @Provides
-        fun provideAppWidgetManager(@Application context: Context): AppWidgetManager {
-            return AppWidgetManager.getInstance(context)
+        fun provideAppWidgetManager(@Application context: Context): Optional<AppWidgetManager> {
+            return Optional.ofNullable(AppWidgetManager.getInstance(context))
         }
 
         @SysUISingleton
@@ -54,7 +55,7 @@
         @SysUISingleton
         @Provides
         fun provideCommunalWidgetHost(
-            appWidgetManager: AppWidgetManager,
+            appWidgetManager: Optional<AppWidgetManager>,
             appWidgetHost: AppWidgetHost,
             @CommunalLog logBuffer: LogBuffer,
         ): CommunalWidgetHost {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalWidgetHost.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalWidgetHost.kt
index 086d729..155de32 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalWidgetHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalWidgetHost.kt
@@ -22,6 +22,7 @@
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.core.Logger
 import com.android.systemui.log.dagger.CommunalLog
+import java.util.Optional
 import javax.inject.Inject
 
 /**
@@ -31,7 +32,7 @@
 class CommunalWidgetHost
 @Inject
 constructor(
-    private val appWidgetManager: AppWidgetManager,
+    private val appWidgetManager: Optional<AppWidgetManager>,
     private val appWidgetHost: AppWidgetHost,
     @CommunalLog logBuffer: LogBuffer,
 ) {
@@ -56,6 +57,10 @@
         return null
     }
 
-    private fun bindWidget(widgetId: Int, provider: ComponentName): Boolean =
-        appWidgetManager.bindAppWidgetIdIfAllowed(widgetId, provider)
+    private fun bindWidget(widgetId: Int, provider: ComponentName): Boolean {
+        if (appWidgetManager.isPresent) {
+            return appWidgetManager.get().bindAppWidgetIdIfAllowed(widgetId, provider)
+        }
+        return false
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarter.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarter.kt
index 846e300..55acad0 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarter.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarter.kt
@@ -19,17 +19,26 @@
 import android.content.Context
 import android.content.Intent
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.plugins.ActivityStarter
+import javax.inject.Inject
 
 interface EditWidgetsActivityStarter {
     fun startActivity()
 }
 
-class EditWidgetsActivityStarterImpl(@Application private val applicationContext: Context) :
-    EditWidgetsActivityStarter {
+class EditWidgetsActivityStarterImpl
+@Inject
+constructor(
+    @Application private val applicationContext: Context,
+    private val activityStarter: ActivityStarter,
+) : EditWidgetsActivityStarter {
+
     override fun startActivity() {
-        applicationContext.startActivity(
+        activityStarter.startActivityDismissingKeyguard(
             Intent(applicationContext, EditWidgetsActivity::class.java)
-                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK),
+            /* onlyProvisioned = */ true,
+            /* dismissShade = */ true,
         )
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/start/ControlsStartable.kt b/packages/SystemUI/src/com/android/systemui/controls/start/ControlsStartable.kt
index 0218f45..20bfbc9 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/start/ControlsStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/start/ControlsStartable.kt
@@ -26,6 +26,8 @@
 import androidx.annotation.WorkerThread
 import com.android.systemui.CoreStartable
 import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.common.data.shared.model.PackageChangeModel
+import com.android.systemui.common.data.repository.PackageChangeRepository
 import com.android.systemui.controls.controller.ControlsController
 import com.android.systemui.controls.dagger.ControlsComponent
 import com.android.systemui.controls.management.ControlsListingController
@@ -33,8 +35,16 @@
 import com.android.systemui.controls.panels.SelectedComponentRepository
 import com.android.systemui.controls.ui.SelectedItem
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.settings.UserTracker
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
 import java.util.concurrent.Executor
 import javax.inject.Inject
 
@@ -53,13 +63,16 @@
 class ControlsStartable
 @Inject
 constructor(
-        @Background private val executor: Executor,
-        private val controlsComponent: ControlsComponent,
-        private val userTracker: UserTracker,
-        private val authorizedPanelsRepository: AuthorizedPanelsRepository,
-        private val selectedComponentRepository: SelectedComponentRepository,
-        private val userManager: UserManager,
-        private val broadcastDispatcher: BroadcastDispatcher,
+    @Application private val scope: CoroutineScope,
+    @Background private val bgDispatcher: CoroutineDispatcher,
+    @Background private val executor: Executor,
+    private val controlsComponent: ControlsComponent,
+    private val userTracker: UserTracker,
+    private val authorizedPanelsRepository: AuthorizedPanelsRepository,
+    private val selectedComponentRepository: SelectedComponentRepository,
+    private val packageChangeRepository: PackageChangeRepository,
+    private val userManager: UserManager,
+    private val broadcastDispatcher: BroadcastDispatcher,
 ) : CoreStartable {
 
     // These two controllers can only be accessed after `start` method once we've checked if the
@@ -78,6 +91,8 @@
             }
         }
 
+    private var packageJob: Job? = null
+
     override fun start() {}
 
     override fun onBootCompleted() {
@@ -94,6 +109,21 @@
         controlsListingController.forceReload()
         selectDefaultPanelIfNecessary()
         bindToPanel()
+        monitorPackageUninstall()
+    }
+
+    private fun monitorPackageUninstall() {
+        packageJob?.cancel()
+        packageJob = packageChangeRepository.packageChanged(userTracker.userHandle)
+            .filter {
+                val selectedPackage =
+                    selectedComponentRepository.getSelectedComponent()?.componentName?.packageName
+                // Selected package was uninstalled
+                (it is PackageChangeModel.Uninstalled) && (it.packageName == selectedPackage)
+            }
+            .onEach { selectedComponentRepository.removeSelectedComponent() }
+            .flowOn(bgDispatcher)
+            .launchIn(scope)
     }
 
     private fun selectDefaultPanelIfNecessary() {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index ac71664..87a736d 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -54,6 +54,7 @@
 import com.android.systemui.stylus.StylusUsiPowerStartable
 import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
 import com.android.systemui.theme.ThemeOverlayController
+import com.android.systemui.unfold.DisplaySwitchLatencyTracker
 import com.android.systemui.usb.StorageNotification
 import com.android.systemui.util.NotificationChannels
 import com.android.systemui.util.StartBinderLoggerModule
@@ -141,6 +142,12 @@
     @ClassKey(LatencyTester::class)
     abstract fun bindLatencyTester(sysui: LatencyTester): CoreStartable
 
+    /** Inject into DisplaySwitchLatencyTracker.  */
+    @Binds
+    @IntoMap
+    @ClassKey(DisplaySwitchLatencyTracker::class)
+    abstract fun bindDisplaySwitchLatencyTracker(sysui: DisplaySwitchLatencyTracker): CoreStartable
+
     /** Inject into NotificationChannels.  */
     @Binds
     @IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index 31ef100..2f937bc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -110,7 +110,7 @@
     val isKeyguardGoingAway: Flow<Boolean>
 
     /** Is the always-on display available to be used? */
-    val isAodAvailable: Flow<Boolean>
+    val isAodAvailable: StateFlow<Boolean>
 
     fun setAodAvailable(value: Boolean)
 
@@ -338,7 +338,7 @@
             .distinctUntilChanged()
 
     private val _isAodAvailable = MutableStateFlow(false)
-    override val isAodAvailable: Flow<Boolean> = _isAodAvailable.asStateFlow()
+    override val isAodAvailable: StateFlow<Boolean> = _isAodAvailable.asStateFlow()
 
     override fun setAodAvailable(value: Boolean) {
         _isAodAvailable.value = value
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 21651ba2..6eb3b64 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -104,7 +104,7 @@
     val dozeTimeTick: Flow<Long> = repository.dozeTimeTick
 
     /** Whether Always-on Display mode is available. */
-    val isAodAvailable: Flow<Boolean> = repository.isAodAvailable
+    val isAodAvailable: StateFlow<Boolean> = repository.isAodAvailable
 
     /** Doze transition information. */
     val dozeTransitionModel: Flow<DozeTransitionModel> = repository.dozeTransitionModel
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 0d5ba64..1e67771 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -18,6 +18,7 @@
 
 import android.os.Build;
 
+import com.android.systemui.common.data.repository.PackageChangeRepository;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.log.LogBuffer;
 import com.android.systemui.log.LogBufferFactory;
@@ -600,4 +601,12 @@
     public static LogBuffer provideQBluetoothTileDialogLogBuffer(LogBufferFactory factory) {
         return factory.create("BluetoothTileDialogLog", 50);
     }
+
+    /** Provides a {@link LogBuffer} for {@link PackageChangeRepository} */
+    @Provides
+    @SysUISingleton
+    @PackageChangeRepoLog
+    public static LogBuffer providePackageChangeRepoLogBuffer(LogBufferFactory factory) {
+        return factory.create("PackageChangeRepo", 50);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/PackageChangeRepoLog.kt
similarity index 73%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
rename to packages/SystemUI/src/com/android/systemui/log/dagger/PackageChangeRepoLog.kt
index efc7431..93b776c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/PackageChangeRepoLog.kt
@@ -14,12 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.di.bound
+package com.android.systemui.log.dagger
 
+import com.android.systemui.common.data.repository.PackageChangeRepository
+import com.android.systemui.log.LogBuffer
 import javax.inject.Qualifier
 
-/** User associated with current custom tile binding. */
+/** A [LogBuffer] for [PackageChangeRepository]. */
 @Qualifier
 @MustBeDocumented
 @Retention(AnnotationRetention.RUNTIME)
-annotation class CustomTileUser
+annotation class PackageChangeRepoLog
diff --git a/packages/SystemUI/src/com/android/systemui/power/shared/model/WakeSleepReason.kt b/packages/SystemUI/src/com/android/systemui/power/shared/model/WakeSleepReason.kt
index faf9fbe..75055668 100644
--- a/packages/SystemUI/src/com/android/systemui/power/shared/model/WakeSleepReason.kt
+++ b/packages/SystemUI/src/com/android/systemui/power/shared/model/WakeSleepReason.kt
@@ -51,7 +51,10 @@
     BIOMETRIC(isTouch = false, PowerManager.WAKE_REASON_BIOMETRIC),
 
     /** Something else happened to wake up or sleep the device. */
-    OTHER(isTouch = false, PowerManager.WAKE_REASON_UNKNOWN);
+    OTHER(isTouch = false, PowerManager.WAKE_REASON_UNKNOWN),
+
+    /** Device goes to sleep due to folding of a foldable device. */
+    FOLD(isTouch = false, PowerManager.GO_TO_SLEEP_REASON_DEVICE_FOLD);
 
     companion object {
         fun fromPowerManagerWakeReason(reason: Int): WakeSleepReason {
@@ -72,6 +75,7 @@
         fun fromPowerManagerSleepReason(reason: Int): WakeSleepReason {
             return when (reason) {
                 PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON -> POWER_BUTTON
+                PowerManager.GO_TO_SLEEP_REASON_DEVICE_FOLD -> FOLD
                 else -> OTHER
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index 47b0624..a45d6f6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -259,7 +259,11 @@
     private State getState(Collection<QSTile> tiles, String spec) {
         for (QSTile tile : tiles) {
             if (spec.equals(tile.getTileSpec())) {
-                return tile.getState().copy();
+                if (tile.isTileReady()) {
+                    return tile.getState().copy();
+                } else {
+                    return null;
+                }
             }
         }
         return null;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
index 5d28c8c..957cb1e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
@@ -319,7 +319,7 @@
 
     override fun dumpProto(systemUIProtoDump: SystemUIProtoDump, args: Array<String>) {
         val data =
-            currentTiles.value.map { it.tile.state }.mapNotNull { it.toProto() }.toTypedArray()
+            currentTiles.value.map { it.tile.state }.mapNotNull { it?.toProto() }.toTypedArray()
         systemUIProtoDump.tiles = data
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt
index 840db26..fc06090 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt
@@ -63,6 +63,10 @@
                     InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE,
                 )
             }
-        activityStarter.postStartActivityDismissingKeyguard(pendingIntent, animationController)
+        activityStarter.startPendingIntentMaybeDismissingKeyguard(
+            pendingIntent,
+            null,
+            animationController
+        )
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt
index 0a9a6d3..bc016bd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt
@@ -158,6 +158,33 @@
             )
     }
 
+    fun logError(
+        tileSpec: TileSpec,
+        message: String,
+        error: Throwable,
+    ) {
+        tileSpec
+            .getLogBuffer()
+            .log(
+                tileSpec.getLogTag(),
+                LogLevel.ERROR,
+                {},
+                { message },
+                error,
+            )
+    }
+
+    fun logCustomTileUserActionDelivered(tileSpec: TileSpec) {
+        tileSpec
+            .getLogBuffer()
+            .log(
+                tileSpec.getLogTag(),
+                LogLevel.DEBUG,
+                {},
+                { "user action delivered to the service" },
+            )
+    }
+
     private fun TileSpec.getLogTag(): String = "${TAG_FORMAT_PREFIX}_${this.spec}"
 
     private fun TileSpec.getLogBuffer(): LogBuffer =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt
index 382cfe2..6c9a8a4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt
@@ -56,7 +56,7 @@
     override fun createTile(tileSpec: String): QSTile? {
         val viewModel: QSTileViewModel =
             when (val spec = TileSpec.create(tileSpec)) {
-                is TileSpec.CustomTileSpec -> null
+                is TileSpec.CustomTileSpec -> createCustomTileViewModel(spec)
                 is TileSpec.PlatformTileSpec -> tileMap[tileSpec]?.get()
                 is TileSpec.Invalid -> null
             }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileInteractor.kt
deleted file mode 100644
index 14bf25d..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileInteractor.kt
+++ /dev/null
@@ -1,40 +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.qs.tiles.impl.custom
-
-import android.os.UserHandle
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
-import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
-import com.android.systemui.qs.tiles.impl.di.QSTileScope
-import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
-
-@QSTileScope
-class CustomTileInteractor @Inject constructor() : QSTileDataInteractor<CustomTileDataModel> {
-
-    override fun tileData(
-        user: UserHandle,
-        triggers: Flow<DataUpdateTrigger>
-    ): Flow<CustomTileDataModel> {
-        TODO("Not yet implemented")
-    }
-
-    override fun availability(user: UserHandle): Flow<Boolean> {
-        TODO("Not yet implemented")
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileMapper.kt
deleted file mode 100644
index e23a5c2..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileMapper.kt
+++ /dev/null
@@ -1,32 +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.qs.tiles.impl.custom
-
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
-import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
-import com.android.systemui.qs.tiles.impl.di.QSTileScope
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
-import javax.inject.Inject
-
-@QSTileScope
-class CustomTileMapper @Inject constructor() : QSTileDataToStateMapper<CustomTileDataModel> {
-
-    override fun map(config: QSTileConfig, data: CustomTileDataModel): QSTileState {
-        TODO("Not yet implemented")
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileUserActionInteractor.kt
deleted file mode 100644
index f34704b..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/CustomTileUserActionInteractor.kt
+++ /dev/null
@@ -1,32 +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.qs.tiles.impl.custom
-
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
-import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
-import com.android.systemui.qs.tiles.impl.di.QSTileScope
-import javax.inject.Inject
-
-@QSTileScope
-class CustomTileUserActionInteractor @Inject constructor() :
-    QSTileUserActionInteractor<CustomTileDataModel> {
-
-    override suspend fun handleInput(input: QSTileInput<CustomTileDataModel>) {
-        TODO("Not yet implemented")
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileComponent.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileComponent.kt
index 88bc8fa..7b099c2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileComponent.kt
@@ -16,7 +16,10 @@
 
 package com.android.systemui.qs.tiles.impl.custom.di
 
+import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTilePackageUpdatesRepository
 import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
+import com.android.systemui.qs.tiles.impl.custom.domain.interactor.CustomTileInteractor
+import com.android.systemui.qs.tiles.impl.custom.domain.interactor.CustomTileServiceInteractor
 import com.android.systemui.qs.tiles.impl.di.QSTileComponent
 import com.android.systemui.qs.tiles.impl.di.QSTileScope
 import dagger.Subcomponent
@@ -25,6 +28,12 @@
 @Subcomponent(modules = [QSTileConfigModule::class, CustomTileModule::class])
 interface CustomTileComponent : QSTileComponent<CustomTileDataModel> {
 
+    fun customTileInterfaceInteractor(): CustomTileServiceInteractor
+
+    fun customTileInteractor(): CustomTileInteractor
+
+    fun customTilePackageUpdatesRepository(): CustomTilePackageUpdatesRepository
+
     @Subcomponent.Builder
     interface Builder {
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt
index ba8b23a..196fa12 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt
@@ -20,14 +20,16 @@
 import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
 import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
 import com.android.systemui.qs.tiles.base.viewmodel.QSTileCoroutineScopeFactory
-import com.android.systemui.qs.tiles.impl.custom.CustomTileInteractor
-import com.android.systemui.qs.tiles.impl.custom.CustomTileMapper
-import com.android.systemui.qs.tiles.impl.custom.CustomTileUserActionInteractor
 import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepository
 import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepositoryImpl
+import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTilePackageUpdatesRepository
+import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTilePackageUpdatesRepositoryImpl
 import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileRepository
 import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileRepositoryImpl
+import com.android.systemui.qs.tiles.impl.custom.domain.CustomTileMapper
 import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
+import com.android.systemui.qs.tiles.impl.custom.domain.interactor.CustomTileDataInteractor
+import com.android.systemui.qs.tiles.impl.custom.domain.interactor.CustomTileUserActionInteractor
 import com.android.systemui.qs.tiles.impl.di.QSTileScope
 import dagger.Binds
 import dagger.Module
@@ -40,7 +42,7 @@
 
     @Binds
     fun bindDataInteractor(
-        dataInteractor: CustomTileInteractor
+        dataInteractor: CustomTileDataInteractor
     ): QSTileDataInteractor<CustomTileDataModel>
 
     @Binds
@@ -58,6 +60,11 @@
 
     @Binds fun bindCustomTileRepository(impl: CustomTileRepositoryImpl): CustomTileRepository
 
+    @Binds
+    abstract fun bindCustomTilePackageUpdatesRepository(
+        impl: CustomTilePackageUpdatesRepositoryImpl
+    ): CustomTilePackageUpdatesRepository
+
     companion object {
 
         @Provides
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileBoundComponent.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileBoundComponent.kt
deleted file mode 100644
index d382d20..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileBoundComponent.kt
+++ /dev/null
@@ -1,36 +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.qs.tiles.impl.custom.di.bound
-
-import android.os.UserHandle
-import dagger.BindsInstance
-import dagger.Subcomponent
-import kotlinx.coroutines.CoroutineScope
-
-/** @see CustomTileBoundScope */
-@CustomTileBoundScope
-@Subcomponent(modules = [CustomTileBoundModule::class])
-interface CustomTileBoundComponent {
-
-    @Subcomponent.Builder
-    interface Builder {
-        @BindsInstance fun user(@CustomTileUser user: UserHandle): Builder
-        @BindsInstance fun coroutineScope(@CustomTileBoundScope scope: CoroutineScope): Builder
-
-        fun build(): CustomTileBoundComponent
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileBoundModule.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileBoundModule.kt
deleted file mode 100644
index 889424a..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileBoundModule.kt
+++ /dev/null
@@ -1,31 +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.qs.tiles.impl.custom.di.bound
-
-import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTilePackageUpdatesRepository
-import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTilePackageUpdatesRepositoryImpl
-import dagger.Binds
-import dagger.Module
-
-@Module
-interface CustomTileBoundModule {
-
-    @Binds
-    fun bindCustomTilePackageUpdatesRepository(
-        impl: CustomTilePackageUpdatesRepositoryImpl
-    ): CustomTilePackageUpdatesRepository
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/CustomTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/CustomTileMapper.kt
new file mode 100644
index 0000000..875079c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/CustomTileMapper.kt
@@ -0,0 +1,130 @@
+/*
+ * 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.qs.tiles.impl.custom.domain
+
+import android.annotation.SuppressLint
+import android.app.IUriGrantsManager
+import android.content.Context
+import android.graphics.drawable.Drawable
+import android.os.UserHandle
+import android.service.quicksettings.Tile
+import android.widget.Button
+import android.widget.Switch
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import javax.inject.Inject
+
+@SysUISingleton
+class CustomTileMapper
+@Inject
+constructor(
+    private val context: Context,
+    private val uriGrantsManager: IUriGrantsManager,
+) : QSTileDataToStateMapper<CustomTileDataModel> {
+
+    override fun map(config: QSTileConfig, data: CustomTileDataModel): QSTileState {
+        val userContext = context.createContextAsUser(UserHandle(data.user.identifier), 0)
+
+        val iconResult =
+            getIconProvider(
+                userContext = userContext,
+                icon = data.tile.icon,
+                callingAppUid = data.callingAppUid,
+                packageName = data.componentName.packageName,
+                defaultIcon = data.defaultTileIcon,
+            )
+
+        return QSTileState.build(iconResult.iconProvider, data.tile.label) {
+            var tileState: Int = data.tile.state
+            if (data.hasPendingBind) {
+                tileState = Tile.STATE_UNAVAILABLE
+            }
+
+            icon = iconResult.iconProvider
+            activationState =
+                if (iconResult.failedToLoad) {
+                    QSTileState.ActivationState.INACTIVE
+                } else {
+                    QSTileState.ActivationState.valueOf(tileState)
+                }
+
+            if (!data.tile.subtitle.isNullOrEmpty()) {
+                secondaryLabel = data.tile.subtitle
+            }
+
+            contentDescription = data.tile.contentDescription
+            stateDescription = data.tile.stateDescription
+
+            if (!data.isToggleable) {
+                sideViewIcon = QSTileState.SideViewIcon.Chevron
+            }
+
+            supportedActions =
+                if (tileState == Tile.STATE_UNAVAILABLE) {
+                    setOf(QSTileState.UserAction.LONG_CLICK)
+                } else {
+                    setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
+                }
+            expandedAccessibilityClass =
+                if (data.isToggleable) {
+                    Switch::class
+                } else {
+                    Button::class
+                }
+        }
+    }
+
+    @SuppressLint("MissingPermission") // android.permission.INTERACT_ACROSS_USERS_FULL
+    private fun getIconProvider(
+        userContext: Context,
+        icon: android.graphics.drawable.Icon?,
+        callingAppUid: Int,
+        packageName: String,
+        defaultIcon: android.graphics.drawable.Icon?,
+    ): IconResult {
+        var failedToLoad = false
+        val drawable: Drawable? =
+            try {
+                icon?.loadDrawableCheckingUriGrant(
+                    userContext,
+                    uriGrantsManager,
+                    callingAppUid,
+                    packageName,
+                )
+            } catch (e: Exception) {
+                failedToLoad = true
+                null
+            } ?: defaultIcon?.loadDrawable(userContext)
+        return IconResult(
+            {
+                drawable?.constantState?.newDrawable()?.let {
+                    Icon.Loaded(it, contentDescription = null)
+                }
+            },
+            failedToLoad,
+        )
+    }
+
+    class IconResult(
+        val iconProvider: () -> Icon?,
+        val failedToLoad: Boolean,
+    )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/entity/CustomTileDataModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/entity/CustomTileDataModel.kt
index f095c01..5b6ff1e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/entity/CustomTileDataModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/entity/CustomTileDataModel.kt
@@ -20,16 +20,14 @@
 import android.graphics.drawable.Icon
 import android.os.UserHandle
 import android.service.quicksettings.Tile
-import com.android.systemui.qs.tiles.impl.custom.di.bound.CustomTileBoundComponent
 
 data class CustomTileDataModel(
     val user: UserHandle,
     val componentName: ComponentName,
     val tile: Tile,
+    val isToggleable: Boolean,
     val callingAppUid: Int,
     val hasPendingBind: Boolean,
-    val shouldShowChevron: Boolean,
-    val defaultTileLabel: CharSequence?,
-    val defaultTileIcon: Icon?,
-    val component: CustomTileBoundComponent,
+    val defaultTileLabel: CharSequence,
+    val defaultTileIcon: Icon,
 )
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt
new file mode 100644
index 0000000..cff95d8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt
@@ -0,0 +1,133 @@
+/*
+ * 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.qs.tiles.impl.custom.domain.interactor
+
+import android.os.UserHandle
+import android.service.quicksettings.Tile
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
+import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepository
+import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTilePackageUpdatesRepository
+import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
+import com.android.systemui.qs.tiles.impl.di.QSTileScope
+import com.android.systemui.user.data.repository.UserRepository
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.launch
+
+@QSTileScope
+@OptIn(ExperimentalCoroutinesApi::class)
+class CustomTileDataInteractor
+@Inject
+constructor(
+    private val tileSpec: TileSpec.CustomTileSpec,
+    private val defaultsRepository: CustomTileDefaultsRepository,
+    private val serviceInteractor: CustomTileServiceInteractor,
+    private val customTileInteractor: CustomTileInteractor,
+    private val packageUpdatesRepository: CustomTilePackageUpdatesRepository,
+    userRepository: UserRepository,
+    @QSTileScope private val tileScope: CoroutineScope,
+) : QSTileDataInteractor<CustomTileDataModel> {
+
+    private val mutableUserFlow = MutableStateFlow(userRepository.getSelectedUserInfo().userHandle)
+    private val bindingFlow =
+        mutableUserFlow
+            .flatMapLatest { user ->
+                ConflatedCallbackFlow.conflatedCallbackFlow {
+                    serviceInteractor.setUser(user)
+
+                    // Wait for the CustomTileInteractor to become initialized first, because
+                    // binding
+                    // the service might access it
+                    customTileInteractor.initForUser(user)
+                    // Bind the TileService for not active tile
+                    serviceInteractor.bindOnStart()
+
+                    packageUpdatesRepository
+                        .getPackageChangesForUser(user)
+                        .onEach {
+                            defaultsRepository.requestNewDefaults(
+                                user,
+                                tileSpec.componentName,
+                                true
+                            )
+                        }
+                        .launchIn(this)
+
+                    send(Unit)
+                    awaitClose { serviceInteractor.unbind() }
+                }
+            }
+            .shareIn(tileScope, SharingStarted.WhileSubscribed())
+
+    init {
+        // Initialize binding once to flush all the pending messages inside
+        // CustomTileServiceInteractor and then unbind if the tile data isn't observed. This ensures
+        // that all the interactors are loaded and warmed up before binding.
+        tileScope.launch { bindingFlow.first() }
+    }
+
+    override fun tileData(
+        user: UserHandle,
+        triggers: Flow<DataUpdateTrigger>
+    ): Flow<CustomTileDataModel> {
+        tileScope.launch { mutableUserFlow.emit(user) }
+        return bindingFlow.combine(triggers) { _, _ -> }.flatMapLatest { dataFlow(user) }
+    }
+
+    private fun dataFlow(user: UserHandle): Flow<CustomTileDataModel> =
+        combine(
+            serviceInteractor.refreshEvents.onStart { emit(Unit) },
+            serviceInteractor.callingAppIds,
+            customTileInteractor.getTiles(user),
+            defaultsRepository.defaults(user).mapNotNull { it as? CustomTileDefaults.Result },
+        ) { _: Unit, callingAppId: Int, tile: Tile, defaults: CustomTileDefaults.Result ->
+            CustomTileDataModel(
+                user = user,
+                componentName = tileSpec.componentName,
+                tile = tile,
+                callingAppUid = callingAppId,
+                hasPendingBind = serviceInteractor.hasPendingBind(),
+                defaultTileLabel = defaults.label,
+                defaultTileIcon = defaults.icon,
+                isToggleable = customTileInteractor.isTileToggleable(),
+            )
+        }
+
+    override fun availability(user: UserHandle): Flow<Boolean> =
+        with(defaultsRepository) {
+            requestNewDefaults(user, tileSpec.componentName)
+            return defaults(user).map { it is CustomTileDefaults.Result }
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt
index 10b012d..fd96fc5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt
@@ -19,12 +19,14 @@
 import android.os.UserHandle
 import android.service.quicksettings.Tile
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepository
 import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileRepository
 import com.android.systemui.qs.tiles.impl.di.QSTileScope
 import javax.inject.Inject
 import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
 import kotlinx.coroutines.channels.BufferOverflow
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableSharedFlow
@@ -32,21 +34,29 @@
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
 
 /** Manages updates of the [Tile] assigned for the current custom tile. */
 @QSTileScope
 class CustomTileInteractor
 @Inject
 constructor(
+    private val tileSpec: TileSpec.CustomTileSpec,
     private val defaultsRepository: CustomTileDefaultsRepository,
     private val customTileRepository: CustomTileRepository,
     @QSTileScope private val tileScope: CoroutineScope,
     @Background private val backgroundContext: CoroutineContext,
 ) {
 
+    private val userMutex = Mutex()
     private val tileUpdates =
         MutableSharedFlow<Tile>(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
 
+    private var currentUser: UserHandle? = null
+    private var updatesJob: Job? = null
+
     /** [Tile] updates. [updateTile] to emit a new one. */
     fun getTiles(user: UserHandle): Flow<Tile> = customTileRepository.getTiles(user)
 
@@ -55,7 +65,7 @@
      *
      * @throws IllegalStateException when the repository stores a tile for another user. This means
      *   the tile hasn't been updated for the current user. Can happen when this is accessed before
-     *   [init] returns.
+     *   [initForUser] returns.
      */
     fun getTile(user: UserHandle): Tile =
         customTileRepository.getTile(user)
@@ -67,45 +77,60 @@
     suspend fun isTileToggleable(): Boolean = customTileRepository.isTileToggleable()
 
     /**
-     * Initializes the repository for the current user. Suspends until it's safe to call [tile]
+     * Initializes the repository for the current user. Suspends until it's safe to call [getTile]
      * which needs at least one of the following:
      * - defaults are loaded;
      * - receive tile update in [updateTile];
      * - restoration happened for a persisted tile.
      */
     suspend fun initForUser(user: UserHandle) {
-        launchUpdates(user)
-        customTileRepository.restoreForTheUserIfNeeded(user, customTileRepository.isTileActive())
-        // Suspend to make sure it gets the tile from one of the sources: restoration, defaults, or
-        // tile update.
-        customTileRepository.getTiles(user).firstOrNull()
+        userMutex.withLock {
+            if (currentUser == user) {
+                return
+            }
+            updatesJob?.cancel()
+            defaultsRepository.requestNewDefaults(user, tileSpec.componentName)
+            launchUpdates(user)
+            customTileRepository.restoreForTheUserIfNeeded(
+                user,
+                customTileRepository.isTileActive()
+            )
+            // Suspend to make sure it gets the tile from one of the sources: restoration, defaults,
+            // or
+            // tile update.
+            customTileRepository.getTiles(user).firstOrNull()
+            currentUser = user
+        }
     }
 
     private fun launchUpdates(user: UserHandle) {
-        tileUpdates
-            .onEach {
-                customTileRepository.updateWithTile(
-                    user,
-                    it,
-                    customTileRepository.isTileActive(),
-                )
+        updatesJob =
+            tileScope.launch {
+                tileUpdates
+                    .onEach {
+                        customTileRepository.updateWithTile(
+                            user,
+                            it,
+                            customTileRepository.isTileActive(),
+                        )
+                    }
+                    .flowOn(backgroundContext)
+                    .launchIn(this)
+                defaultsRepository
+                    .defaults(user)
+                    .onEach {
+                        customTileRepository.updateWithDefaults(
+                            user,
+                            it,
+                            customTileRepository.isTileActive(),
+                        )
+                    }
+                    .flowOn(backgroundContext)
+                    .launchIn(this)
             }
-            .flowOn(backgroundContext)
-            .launchIn(tileScope)
-        defaultsRepository
-            .defaults(user)
-            .onEach {
-                customTileRepository.updateWithDefaults(
-                    user,
-                    it,
-                    customTileRepository.isTileActive(),
-                )
-            }
-            .flowOn(backgroundContext)
-            .launchIn(tileScope)
     }
 
-    /** Updates current [Tile]. Emits a new event in [tiles]. */
+    /** Updates current [Tile]. Emits a new event in [getTiles]. */
     fun updateTile(newTile: Tile) {
         tileUpdates.tryEmit(newTile)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileServiceInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileServiceInteractor.kt
new file mode 100644
index 0000000..acff40f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileServiceInteractor.kt
@@ -0,0 +1,216 @@
+/*
+ * 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.qs.tiles.impl.custom.domain.interactor
+
+import android.app.PendingIntent
+import android.content.ComponentName
+import android.os.IBinder
+import android.os.Process
+import android.os.RemoteException
+import android.os.UserHandle
+import android.service.quicksettings.IQSTileService
+import android.service.quicksettings.Tile
+import android.service.quicksettings.TileService
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.qs.external.CustomTileInterface
+import com.android.systemui.qs.external.TileServiceManager
+import com.android.systemui.qs.external.TileServices
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.base.logging.QSTileLogger
+import com.android.systemui.qs.tiles.impl.di.QSTileScope
+import com.android.systemui.user.data.repository.UserRepository
+import dagger.Lazy
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.channels.produce
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.launch
+
+/**
+ * Communicates with [TileService] via [TileServiceManager] and [IQSTileService]. This interactor is
+ * also responsible for the binding to the [TileService].
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+@QSTileScope
+class CustomTileServiceInteractor
+@Inject
+constructor(
+    private val tileSpec: TileSpec.CustomTileSpec,
+    private val activityStarter: ActivityStarter,
+    private val userActionInteractor: Lazy<CustomTileUserActionInteractor>,
+    private val customTileInteractor: CustomTileInteractor,
+    private val userRepository: UserRepository,
+    private val qsTileLogger: QSTileLogger,
+    private val tileServices: TileServices,
+    @QSTileScope private val tileScope: CoroutineScope,
+) {
+
+    private val tileReceivingInterface = ReceivingInterface()
+    private var tileServiceManager: TileServiceManager? = null
+    private val tileServiceInterface: IQSTileService
+        get() = getTileServiceManager().tileService
+
+    private var currentUser: UserHandle = userRepository.getSelectedUserInfo().userHandle
+    private var destructionJob: Job? = null
+
+    val callingAppIds: Flow<Int>
+        get() = tileReceivingInterface.mutableCallingAppIds
+    val refreshEvents: Flow<Unit>
+        get() = tileReceivingInterface.mutableRefreshEvents
+
+    /** Clears all pending binding for an active tile and binds not active one. */
+    fun bindOnStart() {
+        try {
+            with(getTileServiceManager()) {
+                if (isActiveTile) {
+                    clearPendingBind()
+                } else {
+                    setBindRequested(true)
+                    tileServiceInterface.onStartListening()
+                }
+            }
+        } catch (e: RemoteException) {
+            qsTileLogger.logError(tileSpec, "Binding to the service failed", e)
+        }
+    }
+
+    /** Binds active tile WITHOUT CLEARING pending binds. */
+    fun bindOnClick() {
+        try {
+            with(getTileServiceManager()) {
+                if (isActiveTile) {
+                    setBindRequested(true)
+                    tileServiceInterface.onStartListening()
+                }
+            }
+        } catch (e: RemoteException) {
+            qsTileLogger.logError(tileSpec, "Binding to the service on click failed", e)
+        }
+    }
+
+    /** Releases resources held by the binding and prepares the interactor to be collected */
+    fun unbind() {
+        try {
+            with(userActionInteractor.get()) {
+                clearLastClickedView()
+                tileServiceInterface.onStopListening()
+                revokeToken(false)
+                setShowingDialog(false)
+            }
+            getTileServiceManager().setBindRequested(false)
+        } catch (e: RemoteException) {
+            qsTileLogger.logError(tileSpec, "Unbinding failed", e)
+        }
+    }
+
+    /**
+     * Checks if [TileServiceManager] has a pending [android.service.quicksettings.TileService]
+     * bind.
+     */
+    fun hasPendingBind(): Boolean = getTileServiceManager().hasPendingBind()
+
+    /** Sets a [user] for the custom tile to use. User change triggers service rebinding. */
+    fun setUser(user: UserHandle) {
+        if (user == currentUser) {
+            return
+        }
+        currentUser = user
+        destructionJob?.cancel()
+
+        tileServiceManager = null
+    }
+
+    /** Sends click event to [TileService] using [IQSTileService.onClick]. */
+    fun onClick(token: IBinder) {
+        tileServiceInterface.onClick(token)
+    }
+
+    private fun getTileServiceManager(): TileServiceManager =
+        synchronized(tileServices) {
+            if (tileServiceManager == null) {
+                tileServices
+                    .getTileWrapper(tileReceivingInterface)
+                    .also { destructionJob = createDestructionJob() }
+                    .also { tileServiceManager = it }
+            } else {
+                tileServiceManager!!
+            }
+        }
+
+    /**
+     * This job used to free the resources when the [QSTileScope] coroutine scope gets cancelled by
+     * the View Model.
+     */
+    private fun createDestructionJob(): Job =
+        tileScope.launch {
+            produce<Unit> {
+                awaitClose {
+                    userActionInteractor.get().revokeToken(true)
+                    tileServices.freeService(tileReceivingInterface, getTileServiceManager())
+                    destructionJob = null
+                }
+            }
+        }
+
+    private inner class ReceivingInterface : CustomTileInterface {
+
+        override val user: Int
+            get() = currentUser.identifier
+        override val qsTile: Tile
+            get() = customTileInteractor.getTile(currentUser)
+        override val component: ComponentName = tileSpec.componentName
+
+        val mutableCallingAppIds = MutableStateFlow(Process.INVALID_UID)
+        val mutableRefreshEvents = MutableSharedFlow<Unit>()
+
+        override fun getTileSpec(): String = tileSpec.spec
+
+        override fun refreshState() {
+            tileScope.launch { mutableRefreshEvents.emit(Unit) }
+        }
+
+        override fun updateTileState(tile: Tile, uid: Int) {
+            customTileInteractor.updateTile(tile)
+            mutableCallingAppIds.tryEmit(uid)
+        }
+
+        override fun onDialogShown() {
+            userActionInteractor.get().setShowingDialog(true)
+        }
+
+        override fun onDialogHidden() =
+            with(userActionInteractor.get()) {
+                setShowingDialog(false)
+                revokeToken(true)
+            }
+
+        override fun startActivityAndCollapse(pendingIntent: PendingIntent) {
+            userActionInteractor.get().startActivityAndCollapse(pendingIntent)
+        }
+
+        override fun startUnlockAndRun() {
+            activityStarter.postQSRunnableDismissingKeyguard {
+                tileServiceInterface.onUnlockComplete()
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt
new file mode 100644
index 0000000..c3e1fea
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt
@@ -0,0 +1,193 @@
+/*
+ * 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.qs.tiles.impl.custom.domain.interactor
+
+import android.app.PendingIntent
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.net.Uri
+import android.os.Binder
+import android.os.IBinder
+import android.os.RemoteException
+import android.os.UserHandle
+import android.provider.Settings
+import android.service.quicksettings.TileService
+import android.view.IWindowManager
+import android.view.View
+import android.view.WindowManager
+import androidx.annotation.GuardedBy
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.interactor.QSTileInput
+import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.logging.QSTileLogger
+import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
+import com.android.systemui.qs.tiles.impl.di.QSTileScope
+import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import com.android.systemui.settings.DisplayTracker
+import java.util.concurrent.atomic.AtomicReference
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.withContext
+
+@QSTileScope
+class CustomTileUserActionInteractor
+@Inject
+constructor(
+    private val context: Context,
+    private val tileSpec: TileSpec,
+    private val qsTileLogger: QSTileLogger,
+    private val windowManager: IWindowManager,
+    private val displayTracker: DisplayTracker,
+    private val qsTileIntentUserInputHandler: QSTileIntentUserInputHandler,
+    @Background private val backgroundContext: CoroutineContext,
+    private val serviceInteractor: CustomTileServiceInteractor,
+) : QSTileUserActionInteractor<CustomTileDataModel> {
+
+    private val token: IBinder = Binder()
+
+    @GuardedBy("token") private var isTokenGranted: Boolean = false
+    @GuardedBy("token") private var isShowingDialog: Boolean = false
+    private val lastClickedView: AtomicReference<View> = AtomicReference<View>()
+
+    override suspend fun handleInput(input: QSTileInput<CustomTileDataModel>) =
+        with(input) {
+            when (action) {
+                is QSTileUserAction.Click -> click(action.view, data.tile.activityLaunchForClick)
+                is QSTileUserAction.LongClick ->
+                    longClick(user, action.view, data.componentName, data.tile.state)
+            }
+            qsTileLogger.logCustomTileUserActionDelivered(tileSpec)
+        }
+
+    private fun click(
+        view: View?,
+        activityLaunchForClick: PendingIntent?,
+    ) {
+        grantToken()
+        try {
+            // Bind active tile to deliver user action
+            serviceInteractor.bindOnClick()
+            if (activityLaunchForClick == null) {
+                lastClickedView.set(view)
+                serviceInteractor.onClick(token)
+            } else {
+                qsTileIntentUserInputHandler.handle(view, activityLaunchForClick)
+            }
+        } catch (e: RemoteException) {
+            qsTileLogger.logError(tileSpec, "Failed to deliver click", e)
+        }
+    }
+
+    fun revokeToken(ignoreShownDialog: Boolean) {
+        synchronized(token) {
+            if (isTokenGranted && (ignoreShownDialog || !isShowingDialog)) {
+                try {
+                    windowManager.removeWindowToken(token, displayTracker.defaultDisplayId)
+                } catch (e: RemoteException) {
+                    qsTileLogger.logError(tileSpec, "Failed to remove a window token", e)
+                }
+                isTokenGranted = false
+            }
+        }
+    }
+
+    fun setShowingDialog(isShowingDialog: Boolean) {
+        synchronized(token) { this.isShowingDialog = isShowingDialog }
+    }
+
+    fun startActivityAndCollapse(pendingIntent: PendingIntent) {
+        if (!pendingIntent.isActivity) {
+            return
+        }
+        if (!isTokenGranted) {
+            return
+        }
+        qsTileIntentUserInputHandler.handle(lastClickedView.getAndSet(null), pendingIntent)
+    }
+
+    fun clearLastClickedView() = lastClickedView.set(null)
+
+    private fun grantToken() {
+        synchronized(token) {
+            if (!isTokenGranted) {
+                try {
+                    windowManager.addWindowToken(
+                        token,
+                        WindowManager.LayoutParams.TYPE_QS_DIALOG,
+                        displayTracker.defaultDisplayId,
+                        null /* options */
+                    )
+                } catch (e: RemoteException) {
+                    qsTileLogger.logError(tileSpec, "Failed to grant a window token", e)
+                }
+                isTokenGranted = true
+            }
+        }
+    }
+
+    private suspend fun longClick(
+        user: UserHandle,
+        view: View?,
+        componentName: ComponentName,
+        state: Int
+    ) {
+        val resolvedIntent: Intent? =
+            resolveIntent(
+                    Intent(TileService.ACTION_QS_TILE_PREFERENCES).apply {
+                        setPackage(componentName.packageName)
+                    },
+                    user,
+                )
+                ?.apply {
+                    putExtra(Intent.EXTRA_COMPONENT_NAME, componentName)
+                    putExtra(TileService.EXTRA_STATE, state)
+                }
+        if (resolvedIntent == null) {
+            qsTileIntentUserInputHandler.handle(
+                view,
+                Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
+                    .setData(
+                        Uri.fromParts(IntentFilter.SCHEME_PACKAGE, componentName.packageName, null)
+                    )
+            )
+        } else {
+            qsTileIntentUserInputHandler.handle(view, resolvedIntent)
+        }
+    }
+
+    /**
+     * Returns an intent resolved by [android.content.pm.PackageManager.resolveActivityAsUser] or
+     * null.
+     */
+    private suspend fun resolveIntent(intent: Intent, user: UserHandle): Intent? =
+        withContext(backgroundContext) {
+            val activityInfo =
+                context.packageManager
+                    .resolveActivityAsUser(intent, 0, user.identifier)
+                    ?.activityInfo
+            activityInfo ?: return@withContext null
+            with(activityInfo) {
+                Intent(TileService.ACTION_QS_TILE_PREFERENCES).apply {
+                    setClassName(packageName, name)
+                }
+            }
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
index be1b740..b927e41 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
@@ -32,7 +32,7 @@
  * // TODO(b/http://b/299909989): Clean up legacy mappings after the transition
  */
 data class QSTileState(
-    val icon: () -> Icon,
+    val icon: () -> Icon?,
     val label: CharSequence,
     val activationState: ActivationState,
     val secondaryLabel: CharSequence?,
@@ -60,7 +60,7 @@
             )
         }
 
-        fun build(icon: () -> Icon, label: CharSequence, build: Builder.() -> Unit): QSTileState =
+        fun build(icon: () -> Icon?, label: CharSequence, build: Builder.() -> Unit): QSTileState =
             Builder(icon, label).apply(build).build()
     }
 
@@ -108,7 +108,7 @@
     }
 
     class Builder(
-        var icon: () -> Icon,
+        var icon: () -> Icon?,
         var label: CharSequence,
     ) {
         var activationState: ActivationState = ActivationState.INACTIVE
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt
index ef3df48..226e2fa0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt
@@ -45,7 +45,10 @@
      */
     fun onUserChanged(user: UserHandle)
 
-    /** Triggers the emission of the new [QSTileState] in a [state]. */
+    /**
+     * Triggers the emission of the new [QSTileState] in a [state]. The new value can still be
+     * skipped if there is no change.
+     */
     fun forceUpdate()
 
     /** Notifies underlying logic about user input. */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
index 977df81..4780a2e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
@@ -37,6 +37,7 @@
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.flow.collectIndexed
+import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onEach
@@ -60,27 +61,34 @@
     private val listeningClients: MutableCollection<Any> = mutableSetOf()
 
     // Cancels the jobs when the adapter is no longer alive
-    private var availabilityJob: Job? = null
+    private var tileAdapterJob: Job? = null
     // Cancels the jobs when clients stop listening
     private var stateJob: Job? = null
 
     init {
-        availabilityJob =
+        tileAdapterJob =
             applicationScope.launch {
-                qsTileViewModel.isAvailable.collectIndexed { index, isAvailable ->
-                    if (!isAvailable) {
-                        qsHost.removeTile(tileSpec)
-                    }
-                    // qsTileViewModel.isAvailable flow often starts with isAvailable == true.
-                    // That's
-                    // why we only allow isAvailable == true once and throw an exception afterwards.
-                    if (index > 0 && isAvailable) {
-                        // See com.android.systemui.qs.pipeline.domain.model.AutoAddable for
-                        // additional
-                        // guidance on how to auto add your tile
-                        throw UnsupportedOperationException("Turning on tile is not supported now")
+                launch {
+                    qsTileViewModel.isAvailable.collectIndexed { index, isAvailable ->
+                        if (!isAvailable) {
+                            qsHost.removeTile(tileSpec)
+                        }
+                        // qsTileViewModel.isAvailable flow often starts with isAvailable == true.
+                        // That's
+                        // why we only allow isAvailable == true once and throw an exception
+                        // afterwards.
+                        if (index > 0 && isAvailable) {
+                            // See com.android.systemui.qs.pipeline.domain.model.AutoAddable for
+                            // additional
+                            // guidance on how to auto add your tile
+                            throw UnsupportedOperationException(
+                                "Turning on tile is not supported now"
+                            )
+                        }
                     }
                 }
+                // Warm up tile with some initial state
+                launch { qsTileViewModel.state.first() }
             }
 
         // QSTileHost doesn't call this when userId is initialized
@@ -185,7 +193,7 @@
 
     override fun destroy() {
         stateJob?.cancel()
-        availabilityJob?.cancel()
+        tileAdapterJob?.cancel()
         qsTileViewModel.destroy()
     }
 
@@ -222,8 +230,9 @@
             QSTile.BooleanState().apply {
                 spec = config.tileSpec.spec
                 label = viewModelState.label
-                // This value is synthetic and doesn't have any meaning
-                value = false
+                // This value is synthetic and doesn't have any meaning. It's only needed to satisfy
+                // CTS tests.
+                value = viewModelState.activationState == QSTileState.ActivationState.ACTIVE
 
                 secondaryLabel = viewModelState.secondaryLabel
                 handlesLongClick =
@@ -233,6 +242,7 @@
                     when (val stateIcon = viewModelState.icon()) {
                         is Icon.Loaded -> DrawableIcon(stateIcon.drawable)
                         is Icon.Resource -> ResourceIcon.get(stateIcon.res)
+                        null -> null
                     }
                 }
                 state = viewModelState.activationState.legacyState
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index c9df317..9b8dd0b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -275,7 +275,12 @@
 
                     updateLockscreenNotificationSetting();
                     updatePublicMode();
-                    mPresenter.onUserSwitched(mCurrentUserId);
+                    if (mPresenter != null) {
+                        mPresenter.onUserSwitched(mCurrentUserId);
+                    } else {
+                        Log.w(TAG, "user switch before setup with presenter",
+                                new Exception());
+                    }
 
                     for (UserChangedListener listener : mListeners) {
                         listener.onUserChanged(mCurrentUserId);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 0f640c9..805b44c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -4455,9 +4455,13 @@
 
         mSectionsManager.setHeaderForegroundColors(onSurface, onSurfaceVariant);
 
-        mFooterView.updateColors();
+        if (mFooterView != null) {
+            mFooterView.updateColors();
+        }
 
-        mEmptyShadeView.setTextColors(onSurface, onSurfaceVariant);
+        if (mEmptyShadeView != null) {
+            mEmptyShadeView.setTextColors(onSurface, onSurfaceVariant);
+        }
     }
 
     void goToFullShade(long delay) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index 0c5472f..16334d3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -41,15 +41,14 @@
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
-import com.android.systemui.log.core.LogLevel;
 import com.android.systemui.res.R;
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
 
 import dagger.Lazy;
 
 import java.io.PrintWriter;
-import java.util.ArrayList;
 import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Consumer;
 
 import javax.inject.Inject;
@@ -63,7 +62,8 @@
     private static final boolean DEBUG_AUTH_WITH_ADB = false;
     private static final String AUTH_BROADCAST_KEY = "debug_trigger_auth";
 
-    private final ArrayList<Callback> mCallbacks = new ArrayList<>();
+    private final ConcurrentHashMap.KeySetView<Callback, Boolean> mCallbacks =
+            ConcurrentHashMap.<Callback>newKeySet();
     private final Context mContext;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private final LockPatternUtils mLockPatternUtils;
@@ -157,9 +157,7 @@
     @Override
     public void addCallback(@NonNull Callback callback) {
         Objects.requireNonNull(callback, "Callback must not be null. b/128895449");
-        if (!mCallbacks.contains(callback)) {
-            mCallbacks.add(callback);
-        }
+        mCallbacks.add(callback);
     }
 
     @Override
@@ -221,18 +219,7 @@
     }
 
     private void invokeForEachCallback(Consumer<Callback> consumer) {
-        // Copy the list to allow removal during callback.
-        ArrayList<Callback> copyOfCallbacks = new ArrayList<>(mCallbacks);
-        for (int i = 0; i < copyOfCallbacks.size(); i++) {
-            Callback callback = copyOfCallbacks.get(i);
-            // Temporary fix for b/315731775, callback is null even though only non-null callbacks
-            // are added to the list by addCallback
-            if (callback != null) {
-                consumer.accept(callback);
-            } else {
-                mLogger.log("KeyguardStateController callback is null", LogLevel.DEBUG);
-            }
-        }
+        mCallbacks.forEach(consumer);
     }
 
     private void notifyUnlockedChanged() {
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyLogger.kt b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyLogger.kt
new file mode 100644
index 0000000..76f7609
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyLogger.kt
@@ -0,0 +1,55 @@
+/*
+ * 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
+
+import com.android.systemui.shared.system.SysUiStatsLog
+
+class DisplaySwitchLatencyLogger {
+
+    /**
+     * Based on data present in [displaySwitchLatencyEvent], logs metrics for atom
+     * [DisplaySwitchLatencyTracked]
+     */
+    fun log(displaySwitchLatencyEvent: DisplaySwitchLatencyTracker.DisplaySwitchLatencyEvent) {
+        with(displaySwitchLatencyEvent) {
+            SysUiStatsLog.write(
+                SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED,
+                latencyMs,
+                fromFoldableDeviceState,
+                fromState,
+                fromFocusedAppUid,
+                fromPipAppUid,
+                fromVisibleAppsUid.toIntArray(),
+                fromDensityDpi,
+                toState,
+                toFoldableDeviceState,
+                toFocusedAppUid,
+                toPipAppUid,
+                toVisibleAppsUid.toIntArray(),
+                toDensityDpi,
+                notificationCount,
+                externalDisplayCount,
+                throttlingLevel,
+                vskinTemperatureC,
+                hallSensorToFirstHingeAngleChangeMs,
+                hallSensorToDeviceStateChangeMs,
+                onScreenTurningOnToOnDrawnMs,
+                onDrawnToOnScreenTurnedOnMs,
+            )
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt
new file mode 100644
index 0000000..92a64a6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt
@@ -0,0 +1,248 @@
+/*
+ * 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
+
+import android.content.Context
+import android.util.Log
+import com.android.app.tracing.TraceUtils.instantForTrack
+import com.android.app.tracing.TraceUtils.traceAsync
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+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
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.power.shared.model.ScreenPowerState
+import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.shared.system.SysUiStatsLog
+import com.android.systemui.unfold.dagger.UnfoldSingleThreadBg
+import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
+import com.android.systemui.util.Compile
+import com.android.systemui.util.Utils.isDeviceFoldable
+import com.android.systemui.util.animation.data.repository.AnimationStatusRepository
+import com.android.systemui.util.kotlin.pairwise
+import com.android.systemui.util.time.SystemClock
+import com.android.systemui.util.time.measureTimeMillis
+import java.util.concurrent.Executor
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.asCoroutineDispatcher
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.launch
+
+/**
+ * [DisplaySwitchLatencyTracker] tracks latency and related fields for display switch of a foldable
+ * device. This class populates [DisplaySwitchLatencyEvent] while an ongoing display switch event
+ */
+@SysUISingleton
+class DisplaySwitchLatencyTracker
+@Inject
+constructor(
+    private val context: Context,
+    private val deviceStateRepository: DeviceStateRepository,
+    private val powerInteractor: PowerInteractor,
+    private val unfoldTransitionInteractor: UnfoldTransitionInteractor,
+    private val animationStatusRepository: AnimationStatusRepository,
+    private val keyguardInteractor: KeyguardInteractor,
+    @UnfoldSingleThreadBg private val singleThreadBgExecutor: Executor,
+    @Application private val applicationScope: CoroutineScope,
+    private val displaySwitchLatencyLogger: DisplaySwitchLatencyLogger,
+    private val systemClock: SystemClock
+) : CoreStartable {
+
+    private val backgroundDispatcher = singleThreadBgExecutor.asCoroutineDispatcher()
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    override fun start() {
+        if (!isDeviceFoldable(context)) {
+            return
+        }
+        applicationScope.launch(backgroundDispatcher) {
+            deviceStateRepository.state
+                .pairwise()
+                .filter {
+                    // Start tracking only when the foldable device is
+                    //folding(UNFOLDED/HALF_FOLDED -> FOLDED) or
+                    //unfolding(FOLDED -> HALF_FOLD/UNFOLDED)
+                    foldableDeviceState ->
+                    foldableDeviceState.previousValue == DeviceState.FOLDED ||
+                        foldableDeviceState.newValue == DeviceState.FOLDED
+                }
+                .flatMapLatest { foldableDeviceState ->
+                    flow {
+                        var displaySwitchLatencyEvent = DisplaySwitchLatencyEvent()
+                        val toFoldableDeviceState = foldableDeviceState.newValue.toStatsInt()
+                        displaySwitchLatencyEvent =
+                            displaySwitchLatencyEvent.withBeforeFields(
+                                foldableDeviceState.previousValue.toStatsInt()
+                            )
+
+                        val displaySwitchTimeMs =
+                            measureTimeMillis(systemClock) {
+                                traceAsync(TAG, "displaySwitch") {
+                                    waitForDisplaySwitch(toFoldableDeviceState)
+                                }
+                            }
+
+                        displaySwitchLatencyEvent =
+                            displaySwitchLatencyEvent.withAfterFields(
+                                toFoldableDeviceState,
+                                displaySwitchTimeMs.toInt(),
+                                getCurrentState()
+                            )
+                        emit(displaySwitchLatencyEvent)
+                    }
+                }
+                .collect { displaySwitchLatencyLogger.log(it) }
+        }
+    }
+
+    private fun DeviceState.toStatsInt(): Int =
+        when (this) {
+            DeviceState.FOLDED -> FOLDABLE_DEVICE_STATE_CLOSED
+            DeviceState.HALF_FOLDED -> FOLDABLE_DEVICE_STATE_HALF_OPEN
+            DeviceState.UNFOLDED -> FOLDABLE_DEVICE_STATE_OPEN
+            DeviceState.CONCURRENT_DISPLAY -> FOLDABLE_DEVICE_STATE_FLIPPED
+            else -> FOLDABLE_DEVICE_STATE_UNKNOWN
+        }
+
+    private suspend fun waitForDisplaySwitch(toFoldableDeviceState: Int) {
+        val isTransitionEnabled =
+            unfoldTransitionInteractor.isAvailable &&
+                animationStatusRepository.areAnimationsEnabled().first()
+        if (shouldWaitForScreenOn(toFoldableDeviceState, isTransitionEnabled)) {
+            waitForScreenTurnedOn()
+        } else {
+            traceAsync(TAG, "waitForTransitionStart()") {
+                unfoldTransitionInteractor.waitForTransitionStart()
+            }
+        }
+    }
+
+    private fun shouldWaitForScreenOn(
+        toFoldableDeviceState: Int,
+        isTransitionEnabled: Boolean
+    ): Boolean = (toFoldableDeviceState == FOLDABLE_DEVICE_STATE_CLOSED || !isTransitionEnabled)
+
+    private suspend fun waitForScreenTurnedOn() {
+        traceAsync(TAG, "waitForScreenTurnedOn()") {
+            powerInteractor.screenPowerState.filter { it == ScreenPowerState.SCREEN_ON }.first()
+        }
+    }
+
+    private fun getCurrentState(): Int =
+        when {
+            isStateAod() -> SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TO_STATE__AOD
+            else -> SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TO_STATE__UNKNOWN
+        }
+
+    private fun isStateAod(): Boolean {
+        val lastWakefulnessEvent = powerInteractor.detailedWakefulness.value
+        val isAodEnabled = keyguardInteractor.isAodAvailable.value
+
+        return (lastWakefulnessEvent.isAsleep() &&
+            (lastWakefulnessEvent.lastSleepReason == WakeSleepReason.FOLD) &&
+            isAodEnabled)
+    }
+
+    private inline fun log(msg: () -> String) {
+        if (DEBUG) Log.d(TAG, msg())
+    }
+
+    private fun DisplaySwitchLatencyEvent.withBeforeFields(
+        fromFoldableDeviceState: Int
+    ): DisplaySwitchLatencyEvent {
+        log { "fromFoldableDeviceState=$fromFoldableDeviceState" }
+        instantForTrack(TAG, "fromFoldableDeviceState=$fromFoldableDeviceState")
+
+        return copy(fromFoldableDeviceState = fromFoldableDeviceState)
+    }
+
+    private fun DisplaySwitchLatencyEvent.withAfterFields(
+        toFoldableDeviceState: Int,
+        displaySwitchTimeMs: Int,
+        toState: Int
+    ): DisplaySwitchLatencyEvent {
+        log {
+            "toFoldableDeviceState=$toFoldableDeviceState, " +
+                "toState=$toState, " +
+                "latencyMs=$displaySwitchTimeMs"
+        }
+        instantForTrack(TAG, "toFoldableDeviceState=$toFoldableDeviceState, toState=$toState")
+
+        return copy(
+            toFoldableDeviceState = toFoldableDeviceState,
+            latencyMs = displaySwitchTimeMs,
+            toState = toState
+        )
+    }
+
+    /**
+     * Stores values corresponding to all respective [DisplaySwitchLatencyTrackedField] in a single
+     * event of display switch for foldable devices.
+     *
+     * Once the data is captured in this data class and appropriate to log, it is logged through
+     * [DisplaySwitchLatencyLogger]
+     */
+    data class DisplaySwitchLatencyEvent(
+        val latencyMs: Int = VALUE_UNKNOWN,
+        val fromFoldableDeviceState: Int = FOLDABLE_DEVICE_STATE_UNKNOWN,
+        val fromState: Int = SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__FROM_STATE__UNKNOWN,
+        val fromFocusedAppUid: Int = VALUE_UNKNOWN,
+        val fromPipAppUid: Int = VALUE_UNKNOWN,
+        val fromVisibleAppsUid: Set<Int> = setOf(),
+        val fromDensityDpi: Int = VALUE_UNKNOWN,
+        val toFoldableDeviceState: Int = FOLDABLE_DEVICE_STATE_UNKNOWN,
+        val toState: Int = SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__FROM_STATE__UNKNOWN,
+        val toFocusedAppUid: Int = VALUE_UNKNOWN,
+        val toPipAppUid: Int = VALUE_UNKNOWN,
+        val toVisibleAppsUid: Set<Int> = setOf(),
+        val toDensityDpi: Int = VALUE_UNKNOWN,
+        val notificationCount: Int = VALUE_UNKNOWN,
+        val externalDisplayCount: Int = VALUE_UNKNOWN,
+        val throttlingLevel: Int =
+            SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__THROTTLING_LEVEL__NONE,
+        val vskinTemperatureC: Int = VALUE_UNKNOWN,
+        val hallSensorToFirstHingeAngleChangeMs: Int = VALUE_UNKNOWN,
+        val hallSensorToDeviceStateChangeMs: Int = VALUE_UNKNOWN,
+        val onScreenTurningOnToOnDrawnMs: Int = VALUE_UNKNOWN,
+        val onDrawnToOnScreenTurnedOnMs: Int = VALUE_UNKNOWN
+    )
+
+    companion object {
+        private const val VALUE_UNKNOWN = -1
+        private const val TAG = "DisplaySwitchLatency"
+        private val DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE)
+
+        private const val FOLDABLE_DEVICE_STATE_UNKNOWN =
+            SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__FROM_FOLDABLE_DEVICE_STATE__STATE_UNKNOWN
+        const val FOLDABLE_DEVICE_STATE_CLOSED =
+            SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__FROM_FOLDABLE_DEVICE_STATE__STATE_CLOSED
+        const val FOLDABLE_DEVICE_STATE_HALF_OPEN =
+            SysUiStatsLog
+                .DISPLAY_SWITCH_LATENCY_TRACKED__FROM_FOLDABLE_DEVICE_STATE__STATE_HALF_OPENED
+        private const val FOLDABLE_DEVICE_STATE_OPEN =
+            SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__FROM_FOLDABLE_DEVICE_STATE__STATE_OPENED
+        private const val FOLDABLE_DEVICE_STATE_FLIPPED =
+            SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__FROM_FOLDABLE_DEVICE_STATE__STATE_FLIPPED
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt
index 94912bf8..adf50a1 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt
@@ -22,8 +22,8 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.unfold.data.repository.FoldStateRepository
 import com.android.systemui.unfold.system.DeviceStateRepository
-import com.android.systemui.unfold.updates.FoldStateRepository
 import javax.inject.Inject
 import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.CoroutineScope
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
index 50515da..8bef53c 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
@@ -27,6 +27,8 @@
 import com.android.systemui.unfold.config.UnfoldTransitionConfig
 import com.android.systemui.unfold.dagger.UnfoldBgProgressFlag
 import com.android.systemui.unfold.dagger.UnfoldMain
+import com.android.systemui.unfold.data.repository.FoldStateRepository
+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
@@ -168,6 +170,11 @@
     @Provides
     fun screenStatusProvider(impl: LifecycleScreenStatusProvider): ScreenStatusProvider = impl
 
+    @Provides
+    @Singleton
+    fun provideDisplaySwitchLatencyLogger(): DisplaySwitchLatencyLogger =
+        DisplaySwitchLatencyLogger()
+
     @Module
     interface Bindings {
         @Binds
@@ -178,6 +185,8 @@
         @Binds fun bindRepository(impl: UnfoldTransitionRepositoryImpl): UnfoldTransitionRepository
 
         @Binds fun bindInteractor(impl: UnfoldTransitionInteractorImpl): UnfoldTransitionInteractor
+
+        @Binds fun bindFoldStateRepository(impl: FoldStateRepositoryImpl): FoldStateRepository
     }
 }
 
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateRepository.kt b/packages/SystemUI/src/com/android/systemui/unfold/data/repository/FoldStateRepository.kt
similarity index 84%
rename from packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateRepository.kt
rename to packages/SystemUI/src/com/android/systemui/unfold/data/repository/FoldStateRepository.kt
index 61b0b40..04b00ca 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/data/repository/FoldStateRepository.kt
@@ -13,9 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.systemui.unfold.updates
+package com.android.systemui.unfold.data.repository
 
-import com.android.systemui.unfold.updates.FoldStateRepository.FoldUpdate
+import com.android.systemui.unfold.data.repository.FoldStateRepository.FoldUpdate
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_HALF_OPEN
+import com.android.systemui.unfold.updates.FOLD_UPDATE_START_CLOSING
+import com.android.systemui.unfold.updates.FOLD_UPDATE_START_OPENING
+import com.android.systemui.unfold.updates.FoldStateProvider
 import javax.inject.Inject
 import kotlinx.coroutines.channels.Channel
 import kotlinx.coroutines.channels.awaitClose
@@ -50,7 +56,7 @@
                     FOLD_UPDATE_FINISH_HALF_OPEN -> FINISH_HALF_OPEN
                     FOLD_UPDATE_FINISH_FULL_OPEN -> FINISH_FULL_OPEN
                     FOLD_UPDATE_FINISH_CLOSED -> FINISH_CLOSED
-                    else -> error("FoldUpdateNotFound")
+                    else -> error("Fold update with id $oldId is not supported")
                 }
             }
         }
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 a2e77af..3e2e564 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,16 +15,26 @@
  */
 package com.android.systemui.unfold.domain.interactor
 
-import com.android.systemui.unfold.data.repository.UnfoldTransitionStatus.TransitionFinished
 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.TransitionStarted
 import javax.inject.Inject
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.first
 
+/**
+ * Contains business-logic related to fold-unfold transitions while interacting with
+ * [UnfoldTransitionRepository]
+ */
 interface UnfoldTransitionInteractor {
+    /** 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
@@ -37,4 +47,8 @@
     override suspend fun waitForTransitionFinish() {
         repository.transitionStatus.filter { it is TransitionFinished }.first()
     }
+
+    override suspend fun waitForTransitionStart() {
+        repository.transitionStatus.filter { it is TransitionStarted }.first()
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java
index fa6d055..7861ded 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java
@@ -82,6 +82,14 @@
     }
 
     /**
+     * Returns {@code true} if the device is a foldable device
+     */
+    public static boolean isDeviceFoldable(Context context) {
+        return context.getResources()
+                .getIntArray(com.android.internal.R.array.config_foldedDeviceStates).length != 0;
+    }
+
+    /**
      * Allow the media player to be shown in the QS area, controlled by 2 flags.
      * On by default, but can be disabled by setting either flag to 0/false.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/util/time/MeasureTimeUtil.kt b/packages/SystemUI/src/com/android/systemui/util/time/MeasureTimeUtil.kt
new file mode 100644
index 0000000..f131968
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/time/MeasureTimeUtil.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.util.time
+
+import kotlin.contracts.ExperimentalContracts
+import kotlin.contracts.InvocationKind
+import kotlin.contracts.contract
+
+/**
+ * Executes the given [block] and returns elapsed time using provided [systemClock] in milliseconds.
+ */
+@OptIn(ExperimentalContracts::class)
+inline fun measureTimeMillis(systemClock: SystemClock, block: () -> Unit): Long {
+    contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
+    val start = systemClock.currentTimeMillis()
+    block()
+    return systemClock.currentTimeMillis() - start
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
index 8d06a8f..497c4cb 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
@@ -38,6 +38,8 @@
 import com.android.systemui.volume.VolumeDialogImpl;
 import com.android.systemui.volume.VolumePanelFactory;
 import com.android.systemui.volume.VolumeUI;
+import com.android.systemui.volume.panel.dagger.VolumePanelComponent;
+import com.android.systemui.volume.panel.dagger.factory.VolumePanelComponentFactory;
 
 import dagger.Binds;
 import dagger.Lazy;
@@ -48,9 +50,13 @@
 import dagger.multibindings.IntoSet;
 
 /** Dagger Module for code in the volume package. */
-@Module
+@Module(
+        subcomponents = {
+                VolumePanelComponent.class
+        }
+)
 public interface VolumeModule {
-    /** Starts VolumeUI.  */
+    /** Starts VolumeUI. */
     @Binds
     @IntoMap
     @ClassKey(VolumeUI.class)
@@ -61,11 +67,15 @@
     @IntoSet
     ConfigurationController.ConfigurationListener bindVolumeUIConfigChanges(VolumeUI impl);
 
-    /** */
+    /**  */
     @Binds
     VolumeComponent provideVolumeComponent(VolumeDialogComponent volumeDialogComponent);
 
-    /** */
+    /**  */
+    @Binds
+    VolumePanelComponentFactory bindVolumePanelComponentFactory(VolumePanelComponent.Factory impl);
+
+    /**  */
     @Provides
     static VolumeDialog provideVolumeDialog(
             Context context,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/VolumePanelComponentKey.kt
similarity index 70%
copy from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
copy to packages/SystemUI/src/com/android/systemui/volume/panel/VolumePanelComponentKey.kt
index efc7431..22a74d2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/VolumePanelComponentKey.kt
@@ -14,12 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.di.bound
+package com.android.systemui.volume.panel
 
-import javax.inject.Qualifier
-
-/** User associated with current custom tile binding. */
-@Qualifier
-@MustBeDocumented
-@Retention(AnnotationRetention.RUNTIME)
-annotation class CustomTileUser
+/** Uniquely identifies the [com.android.systemui.volume.panel.ui.VolumePanelComponent]. */
+typealias VolumePanelComponentKey = String
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/CoroutineModule.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/CoroutineModule.kt
new file mode 100644
index 0000000..3ce0bac
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/CoroutineModule.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.volume.panel.dagger
+
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import dagger.Module
+import dagger.Provides
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.SupervisorJob
+
+/** Provides Volume Panel coroutine tools. */
+@Module
+interface CoroutineModule {
+
+    companion object {
+
+        /**
+         * Provides a coroutine scope to use inside [VolumePanelScope].
+         * [com.android.systemui.volume.panel.ui.viewmodel.VolumePanelViewModel] manages the
+         * lifecycle of this scope. It's cancelled when the View Model is destroyed. This helps to
+         * free occupied resources when volume panel is not shown.
+         */
+        @VolumePanelScope
+        @Provides
+        fun provideCoroutineScope(@Application applicationScope: CoroutineScope): CoroutineScope =
+            CoroutineScope(applicationScope.coroutineContext + SupervisorJob())
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/DefaultMultibindsModule.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/DefaultMultibindsModule.kt
new file mode 100644
index 0000000..3660ac1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/DefaultMultibindsModule.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.volume.panel.dagger
+
+import com.android.systemui.volume.panel.VolumePanelComponentKey
+import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria
+import dagger.Module
+import dagger.multibindings.Multibinds
+
+/**
+ * Provides empty multibinding maps for [ComponentAvailabilityCriteria] and [VolumePanelComponent]
+ */
+@Module
+interface DefaultMultibindsModule {
+
+    @Multibinds fun criteriaMap(): Map<VolumePanelComponentKey, ComponentAvailabilityCriteria>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/VolumePanelComponent.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/VolumePanelComponent.kt
new file mode 100644
index 0000000..0a057eb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/VolumePanelComponent.kt
@@ -0,0 +1,59 @@
+/*
+ * 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.volume.panel.dagger
+
+import com.android.systemui.volume.panel.dagger.factory.VolumePanelComponentFactory
+import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import com.android.systemui.volume.panel.domain.DomainModule
+import com.android.systemui.volume.panel.domain.interactor.ComponentsInteractor
+import com.android.systemui.volume.panel.ui.UiModule
+import com.android.systemui.volume.panel.ui.viewmodel.ComponentsLayoutManager
+import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelViewModel
+import dagger.BindsInstance
+import dagger.Subcomponent
+import kotlinx.coroutines.CoroutineScope
+
+/**
+ * Core Volume Panel dagger component. It's managed by [VolumePanelViewModel] and lives alongside
+ * it.
+ */
+@VolumePanelScope
+@Subcomponent(
+    modules =
+        [
+            // Volume Panel infra modules
+            CoroutineModule::class,
+            DefaultMultibindsModule::class,
+            DomainModule::class,
+            UiModule::class,
+            // Components modules
+        ]
+)
+interface VolumePanelComponent {
+
+    fun coroutineScope(): CoroutineScope
+
+    fun componentsInteractor(): ComponentsInteractor
+
+    fun componentsLayoutManager(): ComponentsLayoutManager
+
+    @Subcomponent.Factory
+    interface Factory : VolumePanelComponentFactory {
+
+        override fun create(@BindsInstance viewModel: VolumePanelViewModel): VolumePanelComponent
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/factory/VolumePanelComponentFactory.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/factory/VolumePanelComponentFactory.kt
new file mode 100644
index 0000000..e470c3f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/factory/VolumePanelComponentFactory.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.dagger.factory
+
+import com.android.systemui.volume.panel.dagger.VolumePanelComponent
+import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelViewModel
+import dagger.BindsInstance
+
+/**
+ * Common interface for all [dagger.Subcomponent.Factory] providing [VolumePanelComponent].
+ * [VolumePanelViewModel] uses it to create a new instance of the class.
+ */
+interface VolumePanelComponentFactory {
+
+    fun create(@BindsInstance viewModel: VolumePanelViewModel): VolumePanelComponent
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/scope/VolumePanelScope.kt
similarity index 65%
copy from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
copy to packages/SystemUI/src/com/android/systemui/volume/panel/dagger/scope/VolumePanelScope.kt
index efc7431..e597d11 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/scope/VolumePanelScope.kt
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.di.bound
+package com.android.systemui.volume.panel.dagger.scope
 
-import javax.inject.Qualifier
+import javax.inject.Scope
 
-/** User associated with current custom tile binding. */
-@Qualifier
-@MustBeDocumented
-@Retention(AnnotationRetention.RUNTIME)
-annotation class CustomTileUser
+/**
+ * Volume Panel dependency injection scope. This scope is created alongside Volume Panel and
+ * destroyed when it's lo longer present.
+ */
+@MustBeDocumented @Retention(AnnotationRetention.RUNTIME) @Scope annotation class VolumePanelScope
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/domain/ComponentAvailabilityCriteria.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/domain/ComponentAvailabilityCriteria.kt
new file mode 100644
index 0000000..d9702e4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/domain/ComponentAvailabilityCriteria.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.volume.panel.domain
+
+import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
+
+interface ComponentAvailabilityCriteria {
+
+    /**
+     * Checks if the controller is currently available. Can be used to filter out unwanted
+     * components. For example, hide components for the hardware that is temporarily unavailable.
+     */
+    fun isAvailable(): Flow<Boolean>
+}
+
+@VolumePanelScope
+class AlwaysAvailableCriteria @Inject constructor() : ComponentAvailabilityCriteria {
+
+    override fun isAvailable(): Flow<Boolean> = flowOf(true)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/domain/DomainModule.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/domain/DomainModule.kt
new file mode 100644
index 0000000..7817630
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/domain/DomainModule.kt
@@ -0,0 +1,52 @@
+/*
+ * 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.volume.panel.domain
+
+import com.android.systemui.volume.panel.VolumePanelComponentKey
+import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import com.android.systemui.volume.panel.domain.interactor.ComponentsInteractor
+import com.android.systemui.volume.panel.domain.interactor.ComponentsInteractorImpl
+import dagger.Binds
+import dagger.Module
+import dagger.Provides
+
+/** Domain layer bindings module. */
+@Module
+interface DomainModule {
+
+    @Binds fun bindComponentsInteractor(impl: ComponentsInteractorImpl): ComponentsInteractor
+
+    @Binds
+    fun bindDefaultComponentAvailabilityCriteria(
+        impl: AlwaysAvailableCriteria
+    ): ComponentAvailabilityCriteria
+
+    companion object {
+
+        /**
+         * Enabled components collection. These are the components processed by Volume Panel logic
+         * and possibly shown in the UI.
+         *
+         * There should be a binding in [VolumePanelScope] for [ComponentAvailabilityCriteria] and
+         * [com.android.systemui.volume.panel.ui.VolumePanelComponent] for each component from this
+         * collection.
+         */
+        @Provides
+        @VolumePanelScope
+        fun provideEnabledComponents(): Collection<VolumePanelComponentKey> = setOf()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractor.kt
new file mode 100644
index 0000000..e5b52ea
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractor.kt
@@ -0,0 +1,68 @@
+/*
+ * 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.volume.panel.domain.interactor
+
+import com.android.systemui.volume.panel.VolumePanelComponentKey
+import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria
+import com.android.systemui.volume.panel.domain.model.ComponentModel
+import javax.inject.Inject
+import javax.inject.Provider
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.shareIn
+
+interface ComponentsInteractor {
+
+    /**
+     * Components collection for the UI layer. Uses [ComponentAvailabilityCriteria] to dynamically
+     * determine each component availability.
+     */
+    val components: Flow<Collection<ComponentModel>>
+}
+
+@VolumePanelScope
+class ComponentsInteractorImpl
+@Inject
+constructor(
+    enabledComponents: Collection<VolumePanelComponentKey>,
+    defaultCriteria: Provider<ComponentAvailabilityCriteria>,
+    @VolumePanelScope coroutineScope: CoroutineScope,
+    private val criteriaByKey:
+        Map<
+            VolumePanelComponentKey,
+            @JvmSuppressWildcards
+            Provider<@JvmSuppressWildcards ComponentAvailabilityCriteria>
+        >,
+) : ComponentsInteractor {
+
+    override val components: Flow<Collection<ComponentModel>> =
+        combine(
+                enabledComponents.map { componentKey ->
+                    val componentCriteria = (criteriaByKey[componentKey] ?: defaultCriteria).get()
+                    componentCriteria.isAvailable().map { isAvailable ->
+                        ComponentModel(componentKey, isAvailable = isAvailable)
+                    }
+                }
+            ) {
+                it.asList()
+            }
+            .shareIn(coroutineScope, SharingStarted.Eagerly, replay = 1)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/domain/model/ComponentModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/domain/model/ComponentModel.kt
new file mode 100644
index 0000000..9765713
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/domain/model/ComponentModel.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.volume.panel.domain.model
+
+import com.android.systemui.volume.panel.VolumePanelComponentKey
+
+/**
+ * Represents a current state of the Volume Panel component.
+ *
+ * @property key identifies the component the entity represents.
+ * @property isAvailable is true when the component is supported by the device.
+ */
+data class ComponentModel(
+    val key: VolumePanelComponentKey,
+    val isAvailable: Boolean,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileBoundScope.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/UiModule.kt
similarity index 60%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileBoundScope.kt
rename to packages/SystemUI/src/com/android/systemui/volume/panel/ui/UiModule.kt
index 4a4ba2b..bfa7ef2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileBoundScope.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/UiModule.kt
@@ -14,16 +14,16 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.di.bound
+package com.android.systemui.volume.panel.ui
 
-import javax.inject.Scope
+import com.android.systemui.volume.panel.ui.viewmodel.ComponentsLayoutManager
+import com.android.systemui.volume.panel.ui.viewmodel.DefaultComponentsLayoutManager
+import dagger.Binds
+import dagger.Module
 
-/**
- * Scope annotation for bound custom tile scope. This scope lives when a particular
- * [com.android.systemui.qs.external.CustomTile] is listening and bound to the
- * [android.service.quicksettings.TileService].
- */
-@MustBeDocumented
-@Retention(AnnotationRetention.RUNTIME)
-@Scope
-annotation class CustomTileBoundScope
+/** UI layer bindings module. */
+@Module
+interface UiModule {
+
+    @Binds fun bindSorter(impl: DefaultComponentsLayoutManager): ComponentsLayoutManager
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/model/ComponentState.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/model/ComponentState.kt
new file mode 100644
index 0000000..0a226e2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/model/ComponentState.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.volume.panel.ui.model
+
+import com.android.systemui.volume.panel.VolumePanelComponentKey
+
+/**
+ * State of the [VolumePanelComponent].
+ *
+ * @property key uniquely identifies this component
+ * @property component is an inflated component obtained be the View Model
+ * @property isVisible determines component visibility in the UI
+ */
+data class ComponentState(
+    val key: VolumePanelComponentKey,
+    val isVisible: Boolean,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/model/ComponentsLayout.kt
similarity index 70%
copy from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
copy to packages/SystemUI/src/com/android/systemui/volume/panel/ui/model/ComponentsLayout.kt
index efc7431..5690ac3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/model/ComponentsLayout.kt
@@ -14,12 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.di.bound
+package com.android.systemui.volume.panel.ui.model
 
-import javax.inject.Qualifier
-
-/** User associated with current custom tile binding. */
-@Qualifier
-@MustBeDocumented
-@Retention(AnnotationRetention.RUNTIME)
-annotation class CustomTileUser
+/** Represents components grouping into the layout. */
+data class ComponentsLayout(
+    val contentComponents: List<ComponentState>,
+    val bottomBarComponent: ComponentState,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/model/VolumePanelState.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/model/VolumePanelState.kt
new file mode 100644
index 0000000..399342f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/model/VolumePanelState.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.volume.panel.ui.model
+
+import android.content.res.Configuration
+import android.content.res.Configuration.Orientation
+
+/**
+ * State of the Volume Panel itself.
+ *
+ * @property orientation is current Volume Panel orientation.
+ */
+data class VolumePanelState(
+    @Orientation val orientation: Int,
+    val isVisible: Boolean,
+) {
+    init {
+        require(
+            orientation == Configuration.ORIENTATION_PORTRAIT ||
+                orientation == Configuration.ORIENTATION_LANDSCAPE ||
+                orientation == Configuration.ORIENTATION_UNDEFINED ||
+                orientation == Configuration.ORIENTATION_SQUARE
+        ) {
+            "Unknown orientation: $orientation"
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/ComponentsLayoutManager.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/ComponentsLayoutManager.kt
new file mode 100644
index 0000000..f45401a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/ComponentsLayoutManager.kt
@@ -0,0 +1,50 @@
+/*
+ * 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.volume.panel.ui.viewmodel
+
+import com.android.systemui.volume.panel.ui.model.ComponentState
+import com.android.systemui.volume.panel.ui.model.ComponentsLayout
+import com.android.systemui.volume.panel.ui.model.VolumePanelState
+
+/**
+ * Lays out components to [ComponentsLayout], that UI uses to render the Volume Panel.
+ *
+ * Vertical layout shows the list from top to bottom:
+ * ```
+ * -----
+ * | 1 |
+ * | 2 |
+ * | 3 |
+ * | 4 |
+ * -----
+ * ```
+ *
+ * Horizontal layout shows the list in a grid from, filling the columns first:
+ * ```
+ * ----------
+ * | 1 || 3 |
+ * | 2 || 4 |
+ * ----------
+ * ```
+ */
+interface ComponentsLayoutManager {
+
+    fun layout(
+        volumePanelState: VolumePanelState,
+        components: Collection<ComponentState>,
+    ): ComponentsLayout
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/DefaultComponentsLayoutManager.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/DefaultComponentsLayoutManager.kt
new file mode 100644
index 0000000..cedfaf3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/DefaultComponentsLayoutManager.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.volume.panel.ui.viewmodel
+
+import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import com.android.systemui.volume.panel.ui.model.ComponentState
+import com.android.systemui.volume.panel.ui.model.ComponentsLayout
+import com.android.systemui.volume.panel.ui.model.VolumePanelState
+import javax.inject.Inject
+
+/**
+ * Default [ComponentsLayoutManager]. It places [VolumePanelComponents.BOTTOM_BAR] to
+ * [ComponentsLayout.bottomBarComponent] and everything else to
+ * [ComponentsLayout.contentComponents].
+ */
+@VolumePanelScope
+class DefaultComponentsLayoutManager @Inject constructor() : ComponentsLayoutManager {
+
+    override fun layout(
+        volumePanelState: VolumePanelState,
+        components: Collection<ComponentState>
+    ): ComponentsLayout = TODO("Unimplemented yet")
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt
new file mode 100644
index 0000000..dda361a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt
@@ -0,0 +1,127 @@
+/*
+ * 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.volume.panel.ui.viewmodel
+
+import android.content.Context
+import android.content.res.Resources
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.onConfigChanged
+import com.android.systemui.volume.panel.dagger.VolumePanelComponent
+import com.android.systemui.volume.panel.dagger.factory.VolumePanelComponentFactory
+import com.android.systemui.volume.panel.domain.interactor.ComponentsInteractor
+import com.android.systemui.volume.panel.ui.model.ComponentState
+import com.android.systemui.volume.panel.ui.model.ComponentsLayout
+import com.android.systemui.volume.panel.ui.model.VolumePanelState
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+
+class VolumePanelViewModel(
+    resources: Resources,
+    daggerComponentFactory: VolumePanelComponentFactory,
+    configurationController: ConfigurationController,
+) : ViewModel() {
+
+    private val volumePanelComponent: VolumePanelComponent = daggerComponentFactory.create(this)
+
+    private val scope: CoroutineScope
+        get() = volumePanelComponent.coroutineScope()
+
+    private val componentsInteractor: ComponentsInteractor
+        get() = volumePanelComponent.componentsInteractor()
+
+    private val componentsLayoutManager: ComponentsLayoutManager
+        get() = volumePanelComponent.componentsLayoutManager()
+
+    private val mutablePanelVisibility = MutableStateFlow(true)
+
+    val volumePanelState: StateFlow<VolumePanelState> =
+        combine(
+                configurationController.onConfigChanged.distinctUntilChanged(),
+                mutablePanelVisibility,
+            ) { configuration, isVisible ->
+                VolumePanelState(orientation = configuration.orientation, isVisible = isVisible)
+            }
+            .stateIn(
+                volumePanelComponent.coroutineScope(),
+                SharingStarted.Eagerly,
+                VolumePanelState(
+                    orientation = resources.configuration.orientation,
+                    isVisible = mutablePanelVisibility.value,
+                ),
+            )
+    val mComponentsLayout: Flow<ComponentsLayout> =
+        combine(
+                componentsInteractor.components,
+                volumePanelState,
+            ) { components, scope ->
+                val componentStates =
+                    components.map { model ->
+                        ComponentState(
+                            model.key,
+                            model.isAvailable,
+                        )
+                    }
+                componentsLayoutManager.layout(scope, componentStates)
+            }
+            .shareIn(
+                volumePanelComponent.coroutineScope(),
+                SharingStarted.Eagerly,
+                replay = 1,
+            )
+
+    fun dismissPanel() {
+        scope.launch { mutablePanelVisibility.emit(false) }
+    }
+
+    override fun onCleared() {
+        scope.cancel()
+        super.onCleared()
+    }
+
+    class Factory
+    @Inject
+    constructor(
+        @Application private val context: Context,
+        private val daggerComponentFactory: VolumePanelComponentFactory,
+        private val configurationController: ConfigurationController,
+    ) : ViewModelProvider.Factory {
+
+        @Suppress("UNCHECKED_CAST")
+        override fun <T : ViewModel> create(modelClass: Class<T>): T {
+            check(modelClass == VolumePanelViewModel::class.java)
+            return VolumePanelViewModel(
+                context.resources,
+                daggerComponentFactory,
+                configurationController,
+            )
+                as T
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index d03a898..c5ce856 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -30,6 +30,7 @@
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
 import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_STATE_CANCELLING_RESTARTING;
+import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_STATE_STOPPED;
 import static com.android.keyguard.KeyguardUpdateMonitor.DEFAULT_CANCEL_SIGNAL_TIMEOUT;
 import static com.android.keyguard.KeyguardUpdateMonitor.HAL_POWER_PRESS_TIMEOUT;
 import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED;
@@ -1973,6 +1974,24 @@
     }
 
     @Test
+    public void detectFingerprint_onSuccess_biometricStateStopped() {
+        // GIVEN FP detection is running
+        givenDetectFingerprintWithClearingFingerprintManagerInvocations();
+
+        // WHEN detection is successful
+        ArgumentCaptor<FingerprintManager.FingerprintDetectionCallback> fpDetectCallbackCaptor =
+                ArgumentCaptor.forClass(FingerprintManager.FingerprintDetectionCallback.class);
+        verify(mFingerprintManager).detectFingerprint(
+                any(), fpDetectCallbackCaptor.capture(), any());
+        fpDetectCallbackCaptor.getValue().onFingerprintDetected(0, 0, true);
+        mTestableLooper.processAllMessages();
+
+        // THEN fingerprint detect state should immediately update to STOPPED
+        assertThat(mKeyguardUpdateMonitor.mFingerprintRunningState)
+                .isEqualTo(BIOMETRIC_STATE_STOPPED);
+    }
+
+    @Test
     public void testFingerprintSensorProperties() throws RemoteException {
         mFingerprintAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered(
                 new ArrayList<>());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt
index 8f65fc8..bcef67e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt
@@ -24,19 +24,28 @@
 import android.content.IntentFilter
 import android.content.pm.ApplicationInfo
 import android.content.pm.ServiceInfo
+import android.os.UserHandle
 import android.os.UserManager
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.common.data.repository.fakePackageChangeRepository
+import com.android.systemui.common.data.repository.packageChangeRepository
+import com.android.systemui.common.data.shared.model.PackageChangeModel
 import com.android.systemui.controls.ControlsServiceInfo
 import com.android.systemui.controls.controller.ControlsController
 import com.android.systemui.controls.dagger.ControlsComponent
 import com.android.systemui.controls.management.ControlsListingController
 import com.android.systemui.controls.panels.AuthorizedPanelsRepository
 import com.android.systemui.controls.panels.FakeSelectedComponentRepository
+import com.android.systemui.controls.panels.SelectedComponentRepository
 import com.android.systemui.controls.ui.SelectedItem
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
 import com.android.systemui.settings.UserTracker
+import com.android.systemui.testKosmos
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
@@ -48,6 +57,9 @@
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import java.util.Optional
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -61,10 +73,13 @@
 import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
 
+@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 class ControlsStartableTest : SysuiTestCase() {
 
+    private val kosmos = testKosmos()
+
     @Mock private lateinit var controlsController: ControlsController
     @Mock private lateinit var controlsListingController: ControlsListingController
     @Mock private lateinit var userTracker: UserTracker
@@ -72,7 +87,7 @@
     @Mock private lateinit var userManager: UserManager
     @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
 
-    private val preferredPanelsRepository = FakeSelectedComponentRepository()
+    private lateinit var preferredPanelsRepository: FakeSelectedComponentRepository
 
     private lateinit var fakeExecutor: FakeExecutor
 
@@ -81,8 +96,10 @@
         MockitoAnnotations.initMocks(this)
         whenever(authorizedPanelsRepository.getPreferredPackages()).thenReturn(setOf())
         whenever(userManager.isUserUnlocked(anyInt())).thenReturn(true)
+        whenever(userTracker.userHandle).thenReturn(UserHandle.of(1))
 
         fakeExecutor = FakeExecutor(FakeSystemClock())
+        preferredPanelsRepository = FakeSelectedComponentRepository()
     }
 
     @Test
@@ -306,6 +323,100 @@
         verify(controlsController, never()).setPreferredSelection(any())
     }
 
+    @Test
+    fun testSelectedComponentIsUninstalled() =
+        with(kosmos) {
+            testScope.runTest {
+                val selectedComponent =
+                    SelectedComponentRepository.SelectedComponent(
+                        "panel",
+                        TEST_COMPONENT_PANEL,
+                        isPanel = true
+                    )
+                preferredPanelsRepository.setSelectedComponent(selectedComponent)
+                val activeUser = UserHandle.of(100)
+                whenever(userTracker.userHandle).thenReturn(activeUser)
+
+                createStartable(enabled = true).onBootCompleted()
+                fakeExecutor.runAllReady()
+                runCurrent()
+
+                assertThat(preferredPanelsRepository.getSelectedComponent())
+                    .isEqualTo(selectedComponent)
+                fakePackageChangeRepository.notifyChange(
+                    PackageChangeModel.Uninstalled(
+                        packageName = TEST_PACKAGE_PANEL,
+                        packageUid = UserHandle.getUid(100, 1)
+                    )
+                )
+                runCurrent()
+
+                assertThat(preferredPanelsRepository.getSelectedComponent()).isNull()
+            }
+        }
+
+    @Test
+    fun testSelectedComponentIsChanged() =
+        with(kosmos) {
+            testScope.runTest {
+                val selectedComponent =
+                    SelectedComponentRepository.SelectedComponent(
+                        "panel",
+                        TEST_COMPONENT_PANEL,
+                        isPanel = true
+                    )
+                preferredPanelsRepository.setSelectedComponent(selectedComponent)
+                val activeUser = UserHandle.of(100)
+                whenever(userTracker.userHandle).thenReturn(activeUser)
+
+                createStartable(enabled = true).onBootCompleted()
+                fakeExecutor.runAllReady()
+                runCurrent()
+
+                fakePackageChangeRepository.notifyChange(
+                    PackageChangeModel.Changed(
+                        packageName = TEST_PACKAGE_PANEL,
+                        packageUid = UserHandle.getUid(100, 1)
+                    )
+                )
+                runCurrent()
+
+                assertThat(preferredPanelsRepository.getSelectedComponent())
+                    .isEqualTo(selectedComponent)
+            }
+        }
+
+    @Test
+    fun testOtherPackageIsUninstalled() =
+        with(kosmos) {
+            testScope.runTest {
+                val selectedComponent =
+                    SelectedComponentRepository.SelectedComponent(
+                        "panel",
+                        TEST_COMPONENT_PANEL,
+                        isPanel = true
+                    )
+                preferredPanelsRepository.setSelectedComponent(selectedComponent)
+                val activeUser = UserHandle.of(100)
+                whenever(userTracker.userHandle).thenReturn(activeUser)
+
+                createStartable(enabled = true).onBootCompleted()
+                fakeExecutor.runAllReady()
+                runCurrent()
+
+                fakePackageChangeRepository.notifyChange(
+                    PackageChangeModel.Uninstalled(
+                        packageName = TEST_PACKAGE,
+                        packageUid = UserHandle.getUid(100, 1)
+                    )
+                )
+                runCurrent()
+
+                assertThat(preferredPanelsRepository.getSelectedComponent())
+                    .isEqualTo(selectedComponent)
+            }
+        }
+
     private fun setUpControlsListingControls(listings: List<ControlsServiceInfo>) {
         doAnswer { doReturn(listings).`when`(controlsListingController).getCurrentServices() }
             .`when`(controlsListingController)
@@ -326,11 +437,14 @@
                 }
             }
         return ControlsStartable(
+            kosmos.applicationCoroutineScope,
+            kosmos.testDispatcher,
             fakeExecutor,
             component,
             userTracker,
             authorizedPanelsRepository,
             preferredPanelsRepository,
+            kosmos.packageChangeRepository,
             userManager,
             broadcastDispatcher,
         )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt
index 02d40da..ea2b22c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt
@@ -115,7 +115,7 @@
                 .isEqualTo(
                     "test_spec:\n" +
                         "    QSTileState(" +
-                        "icon=() -> com.android.systemui.common.shared.model.Icon, " +
+                        "icon=() -> com.android.systemui.common.shared.model.Icon?, " +
                         "label=test_data, " +
                         "activationState=INACTIVE, " +
                         "secondaryLabel=null, " +
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 48baeb3..2220756 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -341,7 +341,7 @@
     @Mock private JavaAdapter mJavaAdapter;
     @Mock private CastController mCastController;
     @Mock private SharedNotificationContainerInteractor mSharedNotificationContainerInteractor;
-    @Mock private ActiveNotificationsInteractor mActiveNotificationsInteractor;
+    @Mock protected ActiveNotificationsInteractor mActiveNotificationsInteractor;
     @Mock private KeyguardClockPositionAlgorithm mKeyguardClockPositionAlgorithm;
     @Mock private NaturalScrollingSettingObserver mNaturalScrollingSettingObserver;
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index 28fe8e4..3cbb9bb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -456,11 +456,13 @@
         enableSplitShade(/* enabled= */ true);
 
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
         mNotificationPanelViewController.updateResources();
         assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd)
                 .isEqualTo(R.id.qs_edge_guideline);
 
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(false);
         mNotificationPanelViewController.updateResources();
         assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd)
                 .isEqualTo(ConstraintSet.PARENT_ID);
@@ -469,6 +471,7 @@
     @Test
     public void keyguardStatusView_splitShade_dozing_alwaysDozingOn_isCentered() {
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
         mStatusBarStateController.setState(KEYGUARD);
         enableSplitShade(/* enabled= */ true);
 
@@ -480,6 +483,7 @@
     @Test
     public void keyguardStatusView_splitShade_dozing_alwaysDozingOff_isNotCentered() {
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
         mStatusBarStateController.setState(KEYGUARD);
         enableSplitShade(/* enabled= */ true);
 
@@ -491,6 +495,7 @@
     @Test
     public void keyguardStatusView_splitShade_notDozing_alwaysDozingOn_isNotCentered() {
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
         mStatusBarStateController.setState(KEYGUARD);
         enableSplitShade(/* enabled= */ true);
 
@@ -502,6 +507,7 @@
     @Test
     public void keyguardStatusView_splitShade_pulsing_isNotCentered() {
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
         when(mNotificationListContainer.hasPulsingNotifications()).thenReturn(true);
         mStatusBarStateController.setState(KEYGUARD);
         enableSplitShade(/* enabled= */ true);
@@ -514,6 +520,7 @@
     @Test
     public void keyguardStatusView_splitShade_notPulsing_isNotCentered() {
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
         when(mNotificationListContainer.hasPulsingNotifications()).thenReturn(false);
         mStatusBarStateController.setState(KEYGUARD);
         enableSplitShade(/* enabled= */ true);
@@ -529,6 +536,7 @@
         // The conditions below would make the clock NOT be centered on split shade.
         // On single shade it should always be centered though.
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
         when(mNotificationListContainer.hasPulsingNotifications()).thenReturn(false);
         mStatusBarStateController.setState(KEYGUARD);
         setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ false);
@@ -539,6 +547,7 @@
     @Test
     public void keyguardStatusView_willPlayDelayedDoze_isCentered_thenNot() {
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
         mStatusBarStateController.setState(KEYGUARD);
         enableSplitShade(/* enabled= */ true);
 
@@ -553,6 +562,7 @@
     @Test
     public void keyguardStatusView_willPlayDelayedDoze_notifiesKeyguardMediaController() {
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
         mStatusBarStateController.setState(KEYGUARD);
         enableSplitShade(/* enabled= */ true);
 
@@ -564,6 +574,7 @@
     @Test
     public void keyguardStatusView_willPlayDelayedDoze_isCentered_thenStillCenteredIfNoNotifs() {
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(false);
         mStatusBarStateController.setState(KEYGUARD);
         enableSplitShade(/* enabled= */ true);
 
@@ -700,10 +711,12 @@
         mStatusBarStateController.setState(KEYGUARD);
 
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(false);
         triggerPositionClockAndNotifications();
         verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true);
 
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
         triggerPositionClockAndNotifications();
         verify(mKeyguardStatusViewController).displayClock(SMALL, /* animate */ true);
     }
@@ -715,10 +728,12 @@
         clearInvocations(mKeyguardStatusViewController);
 
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(false);
         triggerPositionClockAndNotifications();
         verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true);
 
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
         triggerPositionClockAndNotifications();
         verify(mKeyguardStatusViewController, times(2))
                 .displayClock(LARGE, /* animate */ true);
@@ -730,6 +745,7 @@
     public void testHasNotifications_switchesToLargeClockWhenEnteringSplitShade() {
         mStatusBarStateController.setState(KEYGUARD);
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
 
         enableSplitShade(/* enabled= */ true);
 
@@ -740,6 +756,7 @@
     public void testNoNotifications_switchesToLargeClockWhenEnteringSplitShade() {
         mStatusBarStateController.setState(KEYGUARD);
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(false);
 
         enableSplitShade(/* enabled= */ true);
 
@@ -752,6 +769,7 @@
         enableSplitShade(/* enabled= */ true);
         clearInvocations(mKeyguardStatusViewController);
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
 
         enableSplitShade(/* enabled= */ false);
 
@@ -764,6 +782,7 @@
         enableSplitShade(/* enabled= */ true);
         clearInvocations(mKeyguardStatusViewController);
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(false);
 
         enableSplitShade(/* enabled= */ false);
 
@@ -777,6 +796,7 @@
         enableSplitShade(/* enabled= */ true);
         when(mMediaDataManager.hasActiveMediaOrRecommendation()).thenReturn(true);
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
         clearInvocations(mKeyguardStatusViewController);
 
         mNotificationPanelViewController.setDozing(/* dozing= */ true, /* animate= */ false);
@@ -791,6 +811,7 @@
         enableSplitShade(/* enabled= */ true);
         when(mMediaDataManager.hasActiveMediaOrRecommendation()).thenReturn(true);
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
         clearInvocations(mKeyguardStatusViewController);
 
         mNotificationPanelViewController.setDozing(/* dozing= */ true, /* animate= */ false);
@@ -847,6 +868,7 @@
         clearInvocations(mKeyguardStatusViewController);
         when(mMediaDataManager.hasActiveMedia()).thenReturn(true);
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
 
         mNotificationPanelViewController.setDozing(true, false);
 
@@ -863,6 +885,7 @@
         when(mResources.getBoolean(R.bool.force_small_clock_on_lockscreen)).thenReturn(true);
         when(mMediaDataManager.hasActiveMedia()).thenReturn(false);
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(false);
         clearInvocations(mKeyguardStatusViewController);
 
         enableSplitShade(/* enabled= */ true);
@@ -881,6 +904,7 @@
         when(mResources.getBoolean(R.bool.force_small_clock_on_lockscreen)).thenReturn(true);
         when(mMediaDataManager.hasActiveMedia()).thenReturn(false);
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(false);
         clearInvocations(mKeyguardStatusViewController);
 
         enableSplitShade(/* enabled= */ true);
@@ -898,11 +922,13 @@
 
         // one notification + media player visible
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(true);
         triggerPositionClockAndNotifications();
         verify(mKeyguardStatusViewController).displayClock(SMALL, /* animate */ true);
 
         // only media player visible
         when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
+        when(mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()).thenReturn(false);
         triggerPositionClockAndNotifications();
         verify(mKeyguardStatusViewController, times(2)).displayClock(SMALL, true);
         verify(mKeyguardStatusViewController, never()).displayClock(LARGE, /* animate */ true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index 0c6f456..757f16c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -598,6 +598,17 @@
 
     @Test
     @EnableFlags(FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS)
+    public void testEarlyUserSwitch() {
+        mLockscreenUserManager =
+                new TestNotificationLockscreenUserManager(mContext);
+        mBackgroundExecutor.runAllReady();
+        mLockscreenUserManager.mUserChangedCallback.onUserChanging(
+                mCurrentUser.id, mContext);
+        // no crash!
+    }
+
+    @Test
+    @EnableFlags(FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS)
     public void testKeyguardManager_noPrivateNotifications() {
         Mockito.clearInvocations(mDevicePolicyManager);
         // User allows notifications
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt
index 255cf6f..9b4a100 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt
@@ -31,6 +31,7 @@
 import com.android.systemui.statusbar.notification.collection.render.NotifStats
 import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
 import com.android.systemui.statusbar.notification.domain.interactor.RenderNotificationListInteractor
+import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
 import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
 import com.android.systemui.statusbar.notification.stack.BUCKET_ALERTING
 import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT
@@ -80,7 +81,7 @@
     }
 
     @Test
-    @DisableFlags(NotificationIconContainerRefactor.FLAG_NAME)
+    @DisableFlags(NotificationIconContainerRefactor.FLAG_NAME, FooterViewRefactor.FLAG_NAME)
     fun testUpdateNotificationIcons() {
         afterRenderListListener.onAfterRenderList(listOf(entry), stackController)
         verify(notificationIconAreaController).updateNotificationIcons(eq(listOf(entry)))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
new file mode 100644
index 0000000..ee2e5ad
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
@@ -0,0 +1,324 @@
+/*
+ * 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
+
+import android.content.Context
+import android.content.res.Resources
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.internal.R
+import com.android.systemui.SysuiTestCase
+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
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.power.shared.model.ScreenPowerState
+import com.android.systemui.power.shared.model.WakeSleepReason
+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.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.util.animation.data.repository.AnimationStatusRepository
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import java.util.Optional
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.asExecutor
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestDispatcher
+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.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
+    private lateinit var displaySwitchLatencyTracker: DisplaySwitchLatencyTracker
+    @Captor private lateinit var loggerArgumentCaptor: ArgumentCaptor<DisplaySwitchLatencyEvent>
+
+    private val mockContext = mock<Context>()
+    private val resources = mock<Resources>()
+    private val foldStateRepository = mock<DeviceStateRepository>()
+    private val powerInteractor = mock<PowerInteractor>()
+    private val animationStatusRepository = mock<AnimationStatusRepository>()
+    private val keyguardInteractor = mock<KeyguardInteractor>()
+    private val displaySwitchLatencyLogger = mock<DisplaySwitchLatencyLogger>()
+
+    private val nonEmptyClosedDeviceStatesArray: IntArray = IntArray(2) { 0 }
+    private val testDispatcher: TestDispatcher = StandardTestDispatcher()
+    private val testScope: TestScope = TestScope(testDispatcher)
+    private val isAsleep = MutableStateFlow(false)
+    private val isAodAvailable = MutableStateFlow(false)
+    private val deviceState = MutableStateFlow(DeviceState.UNFOLDED)
+    private val screenPowerState = MutableStateFlow(ScreenPowerState.SCREEN_ON)
+    private val areAnimationEnabled = MutableStateFlow(true)
+    private val lastWakefulnessEvent = MutableStateFlow(WakefulnessModel())
+    private val systemClock = FakeSystemClock()
+    private val unfoldTransitionProgressProvider = TestUnfoldTransitionProvider()
+    private val unfoldTransitionRepository =
+        UnfoldTransitionRepositoryImpl(Optional.of(unfoldTransitionProgressProvider))
+    private val unfoldTransitionInteractor =
+        UnfoldTransitionInteractorImpl(unfoldTransitionRepository)
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        whenever(mockContext.resources).thenReturn(resources)
+        whenever(resources.getIntArray(R.array.config_foldedDeviceStates))
+            .thenReturn(nonEmptyClosedDeviceStatesArray)
+        whenever(foldStateRepository.state).thenReturn(deviceState)
+        whenever(powerInteractor.isAsleep).thenReturn(isAsleep)
+        whenever(animationStatusRepository.areAnimationsEnabled()).thenReturn(areAnimationEnabled)
+        whenever(powerInteractor.screenPowerState).thenReturn(screenPowerState)
+        whenever(keyguardInteractor.isAodAvailable).thenReturn(isAodAvailable)
+        whenever(powerInteractor.detailedWakefulness).thenReturn(lastWakefulnessEvent)
+
+        displaySwitchLatencyTracker =
+            DisplaySwitchLatencyTracker(
+                mockContext,
+                foldStateRepository,
+                powerInteractor,
+                unfoldTransitionInteractor,
+                animationStatusRepository,
+                keyguardInteractor,
+                testDispatcher.asExecutor(),
+                testScope.backgroundScope,
+                displaySwitchLatencyLogger,
+                systemClock
+            )
+    }
+
+    @Test
+    fun unfold_logsLatencyTillTransitionStarted() {
+        testScope.runTest {
+            areAnimationEnabled.emit(true)
+
+            displaySwitchLatencyTracker.start()
+            deviceState.emit(DeviceState.FOLDED)
+            screenPowerState.emit(ScreenPowerState.SCREEN_OFF)
+            systemClock.advanceTime(50)
+            runCurrent()
+            deviceState.emit(DeviceState.HALF_FOLDED)
+            runCurrent()
+            systemClock.advanceTime(50)
+            screenPowerState.emit(ScreenPowerState.SCREEN_ON)
+            systemClock.advanceTime(200)
+            unfoldTransitionProgressProvider.onTransitionStarted()
+            runCurrent()
+            deviceState.emit(DeviceState.UNFOLDED)
+
+            verify(displaySwitchLatencyLogger).log(capture(loggerArgumentCaptor))
+            val loggedEvent = loggerArgumentCaptor.value
+            val expectedLoggedEvent =
+                DisplaySwitchLatencyEvent(
+                    latencyMs = 250,
+                    fromFoldableDeviceState = FOLDABLE_DEVICE_STATE_CLOSED,
+                    toFoldableDeviceState = FOLDABLE_DEVICE_STATE_HALF_OPEN
+                )
+            assertThat(loggedEvent).isEqualTo(expectedLoggedEvent)
+        }
+    }
+
+    @Test
+    fun unfold_progressUnavailable_logsLatencyTillScreenTurnedOn() {
+        testScope.runTest {
+            val unfoldTransitionInteractorWithEmptyProgressProvider =
+                UnfoldTransitionInteractorImpl(UnfoldTransitionRepositoryImpl(Optional.empty()))
+            displaySwitchLatencyTracker =
+                DisplaySwitchLatencyTracker(
+                    mockContext,
+                    foldStateRepository,
+                    powerInteractor,
+                    unfoldTransitionInteractorWithEmptyProgressProvider,
+                    animationStatusRepository,
+                    keyguardInteractor,
+                    testDispatcher.asExecutor(),
+                    testScope.backgroundScope,
+                    displaySwitchLatencyLogger,
+                    systemClock
+                )
+            areAnimationEnabled.emit(true)
+
+            displaySwitchLatencyTracker.start()
+            deviceState.emit(DeviceState.FOLDED)
+            screenPowerState.emit(ScreenPowerState.SCREEN_OFF)
+            systemClock.advanceTime(50)
+            runCurrent()
+            deviceState.emit(DeviceState.HALF_FOLDED)
+            systemClock.advanceTime(50)
+            runCurrent()
+            screenPowerState.emit(ScreenPowerState.SCREEN_ON)
+            systemClock.advanceTime(50)
+            runCurrent()
+            systemClock.advanceTime(200)
+            unfoldTransitionProgressProvider.onTransitionStarted()
+            runCurrent()
+            deviceState.emit(DeviceState.UNFOLDED)
+
+            verify(displaySwitchLatencyLogger).log(capture(loggerArgumentCaptor))
+            val loggedEvent = loggerArgumentCaptor.value
+            val expectedLoggedEvent =
+                DisplaySwitchLatencyEvent(
+                    latencyMs = 50,
+                    fromFoldableDeviceState = FOLDABLE_DEVICE_STATE_CLOSED,
+                    toFoldableDeviceState = FOLDABLE_DEVICE_STATE_HALF_OPEN
+                )
+            assertThat(loggedEvent).isEqualTo(expectedLoggedEvent)
+        }
+    }
+
+    @Test
+    fun unfold_animationDisabled_logsLatencyTillScreenTurnedOn() {
+        testScope.runTest {
+            areAnimationEnabled.emit(false)
+
+            displaySwitchLatencyTracker.start()
+            deviceState.emit(DeviceState.FOLDED)
+            screenPowerState.emit(ScreenPowerState.SCREEN_OFF)
+            systemClock.advanceTime(50)
+            runCurrent()
+            deviceState.emit(DeviceState.HALF_FOLDED)
+            systemClock.advanceTime(50)
+            runCurrent()
+            screenPowerState.emit(ScreenPowerState.SCREEN_ON)
+            systemClock.advanceTime(50)
+            runCurrent()
+            unfoldTransitionProgressProvider.onTransitionStarted()
+            systemClock.advanceTime(200)
+            runCurrent()
+            deviceState.emit(DeviceState.UNFOLDED)
+
+            verify(displaySwitchLatencyLogger).log(capture(loggerArgumentCaptor))
+            val loggedEvent = loggerArgumentCaptor.value
+            val expectedLoggedEvent =
+                DisplaySwitchLatencyEvent(
+                    latencyMs = 50,
+                    fromFoldableDeviceState = FOLDABLE_DEVICE_STATE_CLOSED,
+                    toFoldableDeviceState = FOLDABLE_DEVICE_STATE_HALF_OPEN
+                )
+            assertThat(loggedEvent).isEqualTo(expectedLoggedEvent)
+        }
+    }
+
+    @Test
+    fun foldWhileStayingAwake_logsLatency() {
+        testScope.runTest {
+            areAnimationEnabled.emit(true)
+            deviceState.emit(DeviceState.UNFOLDED)
+            screenPowerState.emit(ScreenPowerState.SCREEN_ON)
+
+            displaySwitchLatencyTracker.start()
+            deviceState.emit(DeviceState.HALF_FOLDED)
+            systemClock.advanceTime(50)
+            runCurrent()
+            deviceState.emit(DeviceState.FOLDED)
+            screenPowerState.emit(ScreenPowerState.SCREEN_OFF)
+            runCurrent()
+            systemClock.advanceTime(200)
+            screenPowerState.emit(ScreenPowerState.SCREEN_ON)
+            runCurrent()
+
+            verify(displaySwitchLatencyLogger).log(capture(loggerArgumentCaptor))
+            val loggedEvent = loggerArgumentCaptor.value
+            val expectedLoggedEvent =
+                DisplaySwitchLatencyEvent(
+                    latencyMs = 200,
+                    fromFoldableDeviceState = FOLDABLE_DEVICE_STATE_HALF_OPEN,
+                    toFoldableDeviceState = FOLDABLE_DEVICE_STATE_CLOSED
+                )
+            assertThat(loggedEvent).isEqualTo(expectedLoggedEvent)
+        }
+    }
+
+    @Test
+    fun foldToAod_capturesToStateAsAod() {
+        testScope.runTest {
+            areAnimationEnabled.emit(true)
+            deviceState.emit(DeviceState.UNFOLDED)
+            isAodAvailable.emit(true)
+
+            displaySwitchLatencyTracker.start()
+            deviceState.emit(DeviceState.HALF_FOLDED)
+            systemClock.advanceTime(50)
+            runCurrent()
+            deviceState.emit(DeviceState.FOLDED)
+            lastWakefulnessEvent.emit(
+                WakefulnessModel(
+                    internalWakefulnessState = WakefulnessState.ASLEEP,
+                    lastSleepReason = WakeSleepReason.FOLD
+                )
+            )
+            screenPowerState.emit(ScreenPowerState.SCREEN_OFF)
+            runCurrent()
+            systemClock.advanceTime(200)
+            screenPowerState.emit(ScreenPowerState.SCREEN_ON)
+            runCurrent()
+
+            verify(displaySwitchLatencyLogger).log(capture(loggerArgumentCaptor))
+            val loggedEvent = loggerArgumentCaptor.value
+            val expectedLoggedEvent =
+                DisplaySwitchLatencyEvent(
+                    latencyMs = 200,
+                    fromFoldableDeviceState = FOLDABLE_DEVICE_STATE_HALF_OPEN,
+                    toFoldableDeviceState = FOLDABLE_DEVICE_STATE_CLOSED,
+                    toState = SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TO_STATE__AOD
+                )
+            assertThat(loggedEvent).isEqualTo(expectedLoggedEvent)
+        }
+    }
+
+    @Test
+    fun fold_notAFoldable_shouldNotLogLatency() {
+        testScope.runTest {
+            areAnimationEnabled.emit(true)
+            deviceState.emit(DeviceState.UNFOLDED)
+            whenever(resources.getIntArray(R.array.config_foldedDeviceStates))
+                .thenReturn(IntArray(0))
+
+            displaySwitchLatencyTracker.start()
+            deviceState.emit(DeviceState.HALF_FOLDED)
+            systemClock.advanceTime(50)
+            runCurrent()
+            deviceState.emit(DeviceState.FOLDED)
+            screenPowerState.emit(ScreenPowerState.SCREEN_OFF)
+            runCurrent()
+            systemClock.advanceTime(200)
+            screenPowerState.emit(ScreenPowerState.SCREEN_ON)
+            runCurrent()
+
+            verify(displaySwitchLatencyLogger, never()).log(any())
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/FoldStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateRepositoryTest.kt
similarity index 82%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/updates/FoldStateRepositoryTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateRepositoryTest.kt
index 0651323..ab779a7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/FoldStateRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateRepositoryTest.kt
@@ -13,13 +13,20 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.systemui.unfold.updates
+package com.android.systemui.unfold
 
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.unfold.updates.FoldStateRepository.FoldUpdate
+import com.android.systemui.unfold.data.repository.FoldStateRepository.FoldUpdate
+import com.android.systemui.unfold.data.repository.FoldStateRepositoryImpl
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_HALF_OPEN
+import com.android.systemui.unfold.updates.FOLD_UPDATE_START_CLOSING
+import com.android.systemui.unfold.updates.FOLD_UPDATE_START_OPENING
+import com.android.systemui.unfold.updates.FoldStateProvider
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.capture
 import com.android.systemui.util.mockito.mock
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
index 29e737e..d23dae9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
@@ -33,6 +33,7 @@
 import android.testing.TestableLooper;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
 import androidx.core.animation.AndroidXAnimatorIsolationRule;
 import androidx.test.InstrumentationRegistry;
 import androidx.test.uiautomator.UiDevice;
@@ -68,8 +69,20 @@
     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
 
     @Rule
-    public SysuiTestableContext mContext = new SysuiTestableContext(
-            InstrumentationRegistry.getContext(), getLeakCheck());
+    public SysuiTestableContext mContext = createTestableContext();
+
+    @NonNull
+    private SysuiTestableContext createTestableContext() {
+        SysuiTestableContext context = new SysuiTestableContext(
+                InstrumentationRegistry.getContext(), getLeakCheck());
+        if (isRobolectricTest()) {
+            // Manually associate a Display to context for Robolectric test. Similar to b/214297409
+            return context.createDefaultDisplayContext();
+        } else {
+            return context;
+        }
+    }
+
     @Rule
     public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
             new DexmakerShareClassLoaderRule();
@@ -84,10 +97,6 @@
 
     @Before
     public void SysuiSetup() throws Exception {
-        // Manually associate a Display to context for Robolectric test. Similar to b/214297409
-        if (isRobolectricTest()) {
-            mContext = mContext.createDefaultDisplayContext();
-        }
         mSysuiDependency = new SysuiTestDependency(mContext, shouldFailOnLeakedReceiver());
         mDependency = mSysuiDependency.install();
         mRealInstrumentation = InstrumentationRegistry.getInstrumentation();
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/common/data/repository/FakePackageChangeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/data/repository/FakePackageChangeRepository.kt
new file mode 100644
index 0000000..60f0448
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/common/data/repository/FakePackageChangeRepository.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.common.data.repository
+
+import android.os.UserHandle
+import com.android.systemui.common.data.shared.model.PackageChangeModel
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.filter
+
+class FakePackageChangeRepository : PackageChangeRepository {
+
+    private var _packageChanged = MutableSharedFlow<PackageChangeModel>()
+
+    override fun packageChanged(user: UserHandle) =
+        _packageChanged.filter {
+            user == UserHandle.ALL || user == UserHandle.getUserHandleForUid(it.packageUid)
+        }
+
+    suspend fun notifyChange(model: PackageChangeModel) {
+        _packageChanged.emit(model)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/data/repository/PackageChangeRepositoryKosmos.kt
similarity index 66%
copy from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/common/data/repository/PackageChangeRepositoryKosmos.kt
index efc7431..adc05e0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/bound/CustomTileUser.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/common/data/repository/PackageChangeRepositoryKosmos.kt
@@ -14,12 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.tiles.impl.custom.di.bound
+package com.android.systemui.common.data.repository
 
-import javax.inject.Qualifier
+import com.android.systemui.kosmos.Kosmos
 
-/** User associated with current custom tile binding. */
-@Qualifier
-@MustBeDocumented
-@Retention(AnnotationRetention.RUNTIME)
-annotation class CustomTileUser
+var Kosmos.packageChangeRepository: PackageChangeRepository by
+    Kosmos.Fixture { fakePackageChangeRepository }
+val Kosmos.fakePackageChangeRepository by Kosmos.Fixture { FakePackageChangeRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 4200f05..975db3b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -80,7 +80,7 @@
     override val lastDozeTapToWakePosition = _lastDozeTapToWakePosition.asStateFlow()
 
     private val _isAodAvailable = MutableStateFlow(false)
-    override val isAodAvailable: Flow<Boolean> = _isAodAvailable
+    override val isAodAvailable: StateFlow<Boolean> = _isAodAvailable
 
     private val _isDreaming = MutableStateFlow(false)
     override val isDreaming: Flow<Boolean> = _isDreaming
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/CustomTileKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/CustomTileKosmos.kt
index d705248..14f28fe 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/CustomTileKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/CustomTileKosmos.kt
@@ -33,6 +33,7 @@
 val Kosmos.customTileRepository: FakeCustomTileRepository by
     Kosmos.Fixture {
         FakeCustomTileRepository(
+            tileSpec,
             customTileStatePersister,
             packageManagerAdapterFacade,
             testScope.testScheduler,
@@ -46,4 +47,4 @@
     Kosmos.Fixture { FakeCustomTilePackageUpdatesRepository() }
 
 val Kosmos.packageManagerAdapterFacade: FakePackageManagerAdapterFacade by
-    Kosmos.Fixture { FakePackageManagerAdapterFacade(tileSpec) }
+    Kosmos.Fixture { FakePackageManagerAdapterFacade(tileSpec.componentName) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileRepository.kt
index ba803d8..c110da0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileRepository.kt
@@ -19,11 +19,13 @@
 import android.os.UserHandle
 import android.service.quicksettings.Tile
 import com.android.systemui.qs.external.FakeCustomTileStatePersister
+import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
 import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.flow.Flow
 
 class FakeCustomTileRepository(
+    tileSpec: TileSpec.CustomTileSpec,
     customTileStatePersister: FakeCustomTileStatePersister,
     private val packageManagerAdapterFacade: FakePackageManagerAdapterFacade,
     testBackgroundContext: CoroutineContext,
@@ -31,12 +33,16 @@
 
     private val realDelegate: CustomTileRepository =
         CustomTileRepositoryImpl(
-            packageManagerAdapterFacade.tileSpec,
+            tileSpec,
             customTileStatePersister,
             packageManagerAdapterFacade.packageManagerAdapter,
             testBackgroundContext,
         )
 
+    init {
+        require(tileSpec.componentName == packageManagerAdapterFacade.componentName)
+    }
+
     override suspend fun restoreForTheUserIfNeeded(user: UserHandle, isPersistable: Boolean) =
         realDelegate.restoreForTheUserIfNeeded(user, isPersistable)
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakePackageManagerAdapterFacade.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakePackageManagerAdapterFacade.kt
index c9a7655..634d121 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakePackageManagerAdapterFacade.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakePackageManagerAdapterFacade.kt
@@ -16,17 +16,27 @@
 
 package com.android.systemui.qs.tiles.impl.custom.data.repository
 
+import android.content.ComponentName
 import android.content.pm.ServiceInfo
 import android.os.Bundle
 import com.android.systemui.qs.external.PackageManagerAdapter
-import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 
+/**
+ * Facade for [PackageManagerAdapter] to provide a fake-like behaviour. You can create this class
+ * and then get [packageManagerAdapter] to use in your test code.
+ *
+ * This allows to mock [PackageManagerAdapter] to provide a custom behaviour for
+ * [CustomTileRepository.isTileActive], [CustomTileRepository.isTileToggleable],
+ * [com.android.systemui.qs.external.TileServiceManager.isToggleableTile] or
+ * [com.android.systemui.qs.external.TileServiceManager.isActiveTile] when the real objects are
+ * used.
+ */
 class FakePackageManagerAdapterFacade(
-    val tileSpec: TileSpec.CustomTileSpec,
+    val componentName: ComponentName,
     val packageManagerAdapter: PackageManagerAdapter = mock {},
 ) {
 
@@ -34,22 +44,21 @@
     private var isActive: Boolean = false
 
     init {
-        whenever(packageManagerAdapter.getServiceInfo(eq(tileSpec.componentName), any()))
-            .thenAnswer {
-                ServiceInfo().apply {
-                    metaData =
-                        Bundle().apply {
-                            putBoolean(
-                                android.service.quicksettings.TileService.META_DATA_TOGGLEABLE_TILE,
-                                isToggleable
-                            )
-                            putBoolean(
-                                android.service.quicksettings.TileService.META_DATA_ACTIVE_TILE,
-                                isActive
-                            )
-                        }
-                }
+        whenever(packageManagerAdapter.getServiceInfo(eq(componentName), any())).thenAnswer {
+            ServiceInfo().apply {
+                metaData =
+                    Bundle().apply {
+                        putBoolean(
+                            android.service.quicksettings.TileService.META_DATA_TOGGLEABLE_TILE,
+                            isToggleable
+                        )
+                        putBoolean(
+                            android.service.quicksettings.TileService.META_DATA_ACTIVE_TILE,
+                            isActive
+                        )
+                    }
             }
+        }
     }
 
     fun setIsActive(isActive: Boolean) {
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt
index f7fb014..1b7e71a 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt
@@ -27,8 +27,6 @@
 import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder
 import com.android.systemui.unfold.updates.DeviceFoldStateProvider
 import com.android.systemui.unfold.updates.FoldStateProvider
-import com.android.systemui.unfold.updates.FoldStateRepository
-import com.android.systemui.unfold.updates.FoldStateRepositoryImpl
 import com.android.systemui.unfold.updates.RotationChangeProvider
 import com.android.systemui.unfold.updates.hinge.EmptyHingeAngleProvider
 import com.android.systemui.unfold.updates.hinge.HingeAngleProvider
@@ -68,10 +66,6 @@
     fun unfoldKeyguardVisibilityManager(
         impl: UnfoldKeyguardVisibilityManagerImpl
     ): UnfoldKeyguardVisibilityManager = impl
-
-    @Provides
-    @Singleton
-    fun foldStateRepository(impl: FoldStateRepositoryImpl): FoldStateRepository = impl
 }
 
 @Module
diff --git a/services/Android.bp b/services/Android.bp
index 5cb8ec6..0b484f4 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -148,6 +148,9 @@
 java_library {
     name: "Slogf",
     srcs: ["core/java/com/android/server/utils/Slogf.java"],
+    lint: {
+        baseline_filename: "lint-baseline.xml",
+    },
 }
 
 // merge all required services into one jar
@@ -220,6 +223,9 @@
     required: [
         "libukey2_jni_shared",
     ],
+    lint: {
+        baseline_filename: "lint-baseline.xml",
+    },
 
     // Uncomment to enable output of certain warnings (deprecated, unchecked)
     //javacflags: ["-Xlint"],
diff --git a/services/accessibility/Android.bp b/services/accessibility/Android.bp
index e2488a5..a354671 100644
--- a/services/accessibility/Android.bp
+++ b/services/accessibility/Android.bp
@@ -21,6 +21,7 @@
     ],
     lint: {
         error_checks: ["MissingPermissionAnnotation"],
+        baseline_filename: "lint-baseline.xml",
     },
     srcs: [
         ":services.accessibility-sources",
@@ -49,6 +50,9 @@
     libs: [
         "androidx.annotation_annotation",
     ],
+    lint: {
+        baseline_filename: "lint-baseline.xml",
+    },
 }
 
 aconfig_declarations {
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index a19920f..993b254 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -59,13 +59,6 @@
 }
 
 flag {
-    name: "reduce_touch_exploration_sensitivity"
-    namespace: "accessibility"
-    description: "Reduces touch exploration sensitivity by only sending a hover event when the ifnger has moved the amount of pixels defined by the system's touch slop."
-    bug: "303677860"
-}
-
-flag {
     name: "scan_packages_without_lock"
     namespace: "accessibility"
     description: "Scans packages for accessibility service/activity info without holding the A11yMS lock"
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 fc8d4f8..c418485 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -882,22 +882,10 @@
         final int pointerIndex = event.findPointerIndex(pointerId);
         switch (event.getPointerCount()) {
             case 1:
-                // Touch exploration.
+            // Touch exploration.
                 sendTouchExplorationGestureStartAndHoverEnterIfNeeded(policyFlags);
-                if (Flags.reduceTouchExplorationSensitivity()
-                        && mState.getLastInjectedHoverEvent() != null) {
-                    final MotionEvent lastEvent = mState.getLastInjectedHoverEvent();
-                    final float deltaX = lastEvent.getX() - rawEvent.getX();
-                    final float deltaY = lastEvent.getY() - rawEvent.getY();
-                    final double moveDelta = Math.hypot(deltaX, deltaY);
-                    if (moveDelta > mTouchSlop) {
-                        mDispatcher.sendMotionEvent(
-                                event, ACTION_HOVER_MOVE, rawEvent, pointerIdBits, policyFlags);
-                    }
-                } else {
-                    mDispatcher.sendMotionEvent(
-                            event, ACTION_HOVER_MOVE, rawEvent, pointerIdBits, policyFlags);
-                }
+                mDispatcher.sendMotionEvent(
+                        event, ACTION_HOVER_MOVE, rawEvent, pointerIdBits, policyFlags);
                 break;
             case 2:
                 if (mGestureDetector.isMultiFingerGesturesEnabled()
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 203ac2c..df8d9e1 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -2906,7 +2906,13 @@
                 // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
                 if (code == OP_BLUETOOTH_CONNECT) {
                     Slog.e(TAG, "noting OP_BLUETOOTH_CONNECT returned MODE_ERRORED as"
-                            + " #getOpsLocked returned null");
+                            + " #getOpsLocked returned null for"
+                            + " uid: " + uid
+                            + " packageName: " + packageName
+                            + " attributionTag: " + attributionTag
+                            + " pvr.isAttributionTagValid: " + pvr.isAttributionTagValid
+                            + " pvr.bypass: " + pvr.bypass);
+                    Slog.e(TAG, "mUidStates.get(" + uid + "): " + mUidStates.get(uid));
                 }
                 return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
                         packageName);
diff --git a/services/core/java/com/android/server/display/config/DisplayBrightnessMappingConfig.java b/services/core/java/com/android/server/display/config/DisplayBrightnessMappingConfig.java
index 6978686..544f490 100644
--- a/services/core/java/com/android/server/display/config/DisplayBrightnessMappingConfig.java
+++ b/services/core/java/com/android/server/display/config/DisplayBrightnessMappingConfig.java
@@ -118,8 +118,13 @@
                             "The first lux value in the display brightness mapping must be 0");
                 }
 
-                String key = (mapping.getMode() == null ? "default" : mapping.getMode()) + "_"
-                        + (mapping.getSetting() == null ? "normal" : mapping.getSetting());
+                String key = (mapping.getMode() == null
+                        ? AutoBrightnessModeName._default.getRawName()
+                        : mapping.getMode().getRawName())
+                        + "_"
+                        + (mapping.getSetting() == null
+                        ? AutoBrightnessSettingName.normal.getRawName()
+                        : mapping.getSetting().getRawName());
                 if (mBrightnessLevelsMap.containsKey(key)
                         || mBrightnessLevelsLuxMap.containsKey(key)) {
                     throw new IllegalArgumentException(
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 087c525..36dac83 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -48,6 +48,7 @@
 import android.hardware.input.IInputManager;
 import android.hardware.input.IInputSensorEventListener;
 import android.hardware.input.IKeyboardBacklightListener;
+import android.hardware.input.IStickyModifierStateListener;
 import android.hardware.input.ITabletModeChangedListener;
 import android.hardware.input.InputDeviceIdentifier;
 import android.hardware.input.InputManager;
@@ -319,6 +320,9 @@
     // Manages Keyboard backlight
     private final KeyboardBacklightControllerInterface mKeyboardBacklightController;
 
+    // Manages Sticky modifier state
+    private final StickyModifierStateController mStickyModifierStateController;
+
     // Manages Keyboard modifier keys remapping
     private final KeyRemapper mKeyRemapper;
 
@@ -464,6 +468,7 @@
                 ? new KeyboardBacklightController(mContext, mNative, mDataStore,
                         injector.getLooper(), injector.getUEventManager())
                 : new KeyboardBacklightControllerInterface() {};
+        mStickyModifierStateController = new StickyModifierStateController();
         mKeyRemapper = new KeyRemapper(mContext, mNative, mDataStore, injector.getLooper());
 
         mUseDevInputEventForAudioJack =
@@ -2827,6 +2832,33 @@
                         yPosition)).sendToTarget();
     }
 
+    @Override
+    @EnforcePermission(Manifest.permission.MONITOR_STICKY_MODIFIER_STATE)
+    public void registerStickyModifierStateListener(
+            @NonNull IStickyModifierStateListener listener) {
+        super.registerStickyModifierStateListener_enforcePermission();
+        Objects.requireNonNull(listener);
+        mStickyModifierStateController.registerStickyModifierStateListener(listener,
+                Binder.getCallingPid());
+    }
+
+    @Override
+    @EnforcePermission(Manifest.permission.MONITOR_STICKY_MODIFIER_STATE)
+    public void unregisterStickyModifierStateListener(
+            @NonNull IStickyModifierStateListener listener) {
+        super.unregisterStickyModifierStateListener_enforcePermission();
+        Objects.requireNonNull(listener);
+        mStickyModifierStateController.unregisterStickyModifierStateListener(listener,
+                Binder.getCallingPid());
+    }
+
+    // Native callback
+    @SuppressWarnings("unused")
+    void notifyStickyModifierStateChanged(int modifierState, int lockedModifierState) {
+        mStickyModifierStateController.notifyStickyModifierStateChanged(modifierState,
+                lockedModifierState);
+    }
+
     // Native callback.
     @SuppressWarnings("unused")
     boolean isInputMethodConnectionActive() {
diff --git a/services/core/java/com/android/server/input/StickyModifierStateController.java b/services/core/java/com/android/server/input/StickyModifierStateController.java
new file mode 100644
index 0000000..5a22c10
--- /dev/null
+++ b/services/core/java/com/android/server/input/StickyModifierStateController.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 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.server.input;
+
+import android.annotation.BinderThread;
+import android.hardware.input.IStickyModifierStateListener;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+
+/**
+ * A thread-safe component of {@link InputManagerService} responsible for managing the sticky
+ * modifier state for A11y Sticky keys feature.
+ */
+final class StickyModifierStateController {
+
+    private static final String TAG = "ModifierStateController";
+
+    // To enable these logs, run:
+    // 'adb shell setprop log.tag.ModifierStateController DEBUG' (requires restart)
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    // List of currently registered sticky modifier state listeners
+    @GuardedBy("mStickyModifierStateListenerRecords")
+    private final SparseArray<StickyModifierStateListenerRecord>
+            mStickyModifierStateListenerRecords = new SparseArray<>();
+
+    public void notifyStickyModifierStateChanged(int modifierState, int lockedModifierState) {
+        if (DEBUG) {
+            Slog.d(TAG, "Sticky modifier state changed, modifierState = " + modifierState
+                    + ", lockedModifierState = " + lockedModifierState);
+        }
+
+        synchronized (mStickyModifierStateListenerRecords) {
+            for (int i = 0; i < mStickyModifierStateListenerRecords.size(); i++) {
+                mStickyModifierStateListenerRecords.valueAt(i).notifyStickyModifierStateChanged(
+                        modifierState, lockedModifierState);
+            }
+        }
+    }
+
+    /** Register the sticky modifier state listener for a process. */
+    @BinderThread
+    public void registerStickyModifierStateListener(IStickyModifierStateListener listener,
+            int pid) {
+        synchronized (mStickyModifierStateListenerRecords) {
+            if (mStickyModifierStateListenerRecords.get(pid) != null) {
+                throw new IllegalStateException("The calling process has already registered "
+                        + "a StickyModifierStateListener.");
+            }
+            StickyModifierStateListenerRecord record = new StickyModifierStateListenerRecord(pid,
+                    listener);
+            try {
+                listener.asBinder().linkToDeath(record, 0);
+            } catch (RemoteException ex) {
+                throw new RuntimeException(ex);
+            }
+            mStickyModifierStateListenerRecords.put(pid, record);
+        }
+    }
+
+    /** Unregister the sticky modifier state listener for a process. */
+    @BinderThread
+    public void unregisterStickyModifierStateListener(IStickyModifierStateListener listener,
+            int pid) {
+        synchronized (mStickyModifierStateListenerRecords) {
+            StickyModifierStateListenerRecord record = mStickyModifierStateListenerRecords.get(pid);
+            if (record == null) {
+                throw new IllegalStateException("The calling process has no registered "
+                        + "StickyModifierStateListener.");
+            }
+            if (record.mListener.asBinder() != listener.asBinder()) {
+                throw new IllegalStateException("The calling process has a different registered "
+                        + "StickyModifierStateListener.");
+            }
+            record.mListener.asBinder().unlinkToDeath(record, 0);
+            mStickyModifierStateListenerRecords.remove(pid);
+        }
+    }
+
+    private void onStickyModifierStateListenerDied(int pid) {
+        synchronized (mStickyModifierStateListenerRecords) {
+            mStickyModifierStateListenerRecords.remove(pid);
+        }
+    }
+
+    // A record of a registered sticky modifier state listener from one process.
+    private class StickyModifierStateListenerRecord implements IBinder.DeathRecipient {
+        public final int mPid;
+        public final IStickyModifierStateListener mListener;
+
+        StickyModifierStateListenerRecord(int pid, IStickyModifierStateListener listener) {
+            mPid = pid;
+            mListener = listener;
+        }
+
+        @Override
+        public void binderDied() {
+            if (DEBUG) {
+                Slog.d(TAG, "Sticky modifier state listener for pid " + mPid + " died.");
+            }
+            onStickyModifierStateListenerDied(mPid);
+        }
+
+        public void notifyStickyModifierStateChanged(int modifierState, int lockedModifierState) {
+            try {
+                mListener.onStickyModifierStateChanged(modifierState, lockedModifierState);
+            } catch (RemoteException ex) {
+                Slog.w(TAG, "Failed to notify process " + mPid
+                        + " that sticky modifier state changed, assuming it died.", ex);
+                binderDied();
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/notification/ZenAdapters.java b/services/core/java/com/android/server/notification/ZenAdapters.java
index 2a65aff..91df04c 100644
--- a/services/core/java/com/android/server/notification/ZenAdapters.java
+++ b/services/core/java/com/android/server/notification/ZenAdapters.java
@@ -16,6 +16,7 @@
 
 package com.android.server.notification;
 
+import android.app.Flags;
 import android.app.NotificationManager.Policy;
 import android.service.notification.ZenModeConfig;
 import android.service.notification.ZenPolicy;
@@ -57,6 +58,12 @@
                     .showStatusBarIcons(policy.showStatusBarIcons());
         }
 
+        if (Flags.modesApi()) {
+            zenPolicyBuilder.allowChannels(
+                    policy.allowPriorityChannels()
+                            ? ZenPolicy.CHANNEL_TYPE_PRIORITY : ZenPolicy.CHANNEL_TYPE_NONE);
+        }
+
         return zenPolicyBuilder.build();
     }
 
diff --git a/services/core/java/com/android/server/pm/BroadcastHelper.java b/services/core/java/com/android/server/pm/BroadcastHelper.java
index e830d28..e984e9c 100644
--- a/services/core/java/com/android/server/pm/BroadcastHelper.java
+++ b/services/core/java/com/android/server/pm/BroadcastHelper.java
@@ -262,6 +262,7 @@
             // Deliver LOCKED_BOOT_COMPLETED first
             Intent lockedBcIntent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED)
                     .setPackage(packageName);
+            lockedBcIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
             if (includeStopped) {
                 lockedBcIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
             }
@@ -275,6 +276,7 @@
             // Deliver BOOT_COMPLETED only if user is unlocked
             if (mUmInternal.isUserUnlockingOrUnlocked(userId)) {
                 Intent bcIntent = new Intent(Intent.ACTION_BOOT_COMPLETED).setPackage(packageName);
+                bcIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
                 if (includeStopped) {
                     bcIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
                 }
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 3cb2420..0555d90 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -78,6 +78,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.AuxiliaryResolveInfo;
 import android.content.pm.ComponentInfo;
+import android.content.pm.Flags;
 import android.content.pm.InstallSourceInfo;
 import android.content.pm.InstantAppRequest;
 import android.content.pm.InstantAppResolveInfo;
@@ -1511,6 +1512,13 @@
             packageInfo.packageName = packageInfo.applicationInfo.packageName =
                     resolveExternalPackageName(p);
 
+            if (Flags.provideInfoOfApkInApex()) {
+                final String apexModuleName =  ps.getApexModuleName();
+                if (apexModuleName != null) {
+                    packageInfo.setApexPackageName(
+                            mApexManager.getActivePackageNameForApexModuleName(apexModuleName));
+                }
+            }
             return packageInfo;
         } else if ((flags & (MATCH_UNINSTALLED_PACKAGES | MATCH_ARCHIVED_PACKAGES)) != 0
                 && PackageUserStateUtils.isAvailable(state, flags)) {
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index c920ca8..588c629 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -487,7 +487,7 @@
                     // Do not uninstall the APK if an app should be cached
                     boolean keepUninstalledPackage =
                             mPm.shouldKeepUninstalledPackageLPr(packageName);
-                    if (ps.isInstalledOrHasDataOnAnyOtherUser(
+                    if (ps.isInstalledOnAnyOtherUser(
                             mUserManagerInternal.getUserIds(), userId) || keepUninstalledPackage) {
                         // Other users still have this package installed, so all
                         // we need to do is clear this user's data and save that
@@ -533,7 +533,7 @@
                 // artifacts are not stored in the same directory as the APKs
                 deleteArtDexoptArtifacts(packageName);
             }
-            deleteInstalledPackageLIF(ps, deleteCodeAndResources, flags, allUserHandles,
+            deleteInstalledPackageLIF(ps, userId, deleteCodeAndResources, flags, allUserHandles,
                     outInfo, writeSettings);
         }
 
@@ -554,7 +554,7 @@
     }
 
     @GuardedBy("mPm.mInstallLock")
-    private void deleteInstalledPackageLIF(PackageSetting ps,
+    private void deleteInstalledPackageLIF(PackageSetting ps, int userId,
             boolean deleteCodeAndResources, int flags, @NonNull int[] allUserHandles,
             @NonNull PackageRemovedInfo outInfo, boolean writeSettings) {
         synchronized (mPm.mLock) {
@@ -567,7 +567,7 @@
 
         // Delete package data from internal structures and also remove data if flag is set
         mRemovePackageHelper.removePackageDataLIF(
-                ps, allUserHandles, outInfo, flags, writeSettings);
+                ps, userId, allUserHandles, outInfo, flags, writeSettings);
 
         // Delete application code and resources only for parent packages
         if (deleteCodeAndResources) {
@@ -677,8 +677,8 @@
             flags |= PackageManager.DELETE_KEEP_DATA;
         }
         synchronized (mPm.mInstallLock) {
-            deleteInstalledPackageLIF(deletedPs, true, flags, allUserHandles, outInfo,
-                    writeSettings);
+            deleteInstalledPackageLIF(deletedPs, UserHandle.USER_ALL, true, flags, allUserHandles,
+                    outInfo, writeSettings);
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index b638d30..992d8eb 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -3264,9 +3264,9 @@
                 Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());
             }
         }
-        mAppDataHelper.prepareAppDataAfterInstallLIF(pkg);
-
         setPackageInstalledForSystemPackage(pkg, allUserHandles, origUserHandles, writeSettings);
+
+        mAppDataHelper.prepareAppDataAfterInstallLIF(pkg);
     }
 
     private void setPackageInstalledForSystemPackage(@NonNull AndroidPackage pkg,
@@ -4590,7 +4590,9 @@
 
     private void assertPackageWithSharedUserIdIsPrivileged(AndroidPackage pkg)
             throws PackageManagerException {
-        if (!AndroidPackageLegacyUtils.isPrivileged(pkg) && (pkg.getSharedUserId() != null)) {
+        if (!AndroidPackageLegacyUtils.isPrivileged(pkg)
+                && (pkg.getSharedUserId() != null)
+                && !pkg.isLeavingSharedUser()) {
             SharedUserSetting sharedUserSetting = null;
             try {
                 synchronized (mPm.mLock) {
@@ -4630,7 +4632,8 @@
         if (((scanFlags & SCAN_AS_PRIVILEGED) == 0)
                 && !AndroidPackageLegacyUtils.isPrivileged(pkg)
                 && (pkg.getSharedUserId() != null)
-                && !skipVendorPrivilegeScan) {
+                && !skipVendorPrivilegeScan
+                && !pkg.isLeavingSharedUser()) {
             SharedUserSetting sharedUserSetting = null;
             synchronized (mPm.mLock) {
                 try {
diff --git a/services/core/java/com/android/server/pm/ModuleInfoProvider.java b/services/core/java/com/android/server/pm/ModuleInfoProvider.java
index 230f555..6561d46 100644
--- a/services/core/java/com/android/server/pm/ModuleInfoProvider.java
+++ b/services/core/java/com/android/server/pm/ModuleInfoProvider.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
+import android.content.pm.Flags;
 import android.content.pm.IPackageManager;
 import android.content.pm.ModuleInfo;
 import android.content.pm.PackageInfo;
@@ -165,6 +166,10 @@
                 mi.setApexModuleName(
                         mApexManager.getApexModuleNameForPackageName(modulePackageName));
 
+                if (Flags.provideInfoOfApkInApex()) {
+                    mi.setApkInApexPackageNames(mApexManager.getApksInApex(modulePackageName));
+                }
+
                 mModuleInfo.put(modulePackageName, mi);
             }
         } catch (XmlPullParserException | IOException e) {
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 02ce159..45fc49a 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -825,7 +825,7 @@
         return changed;
     }
 
-    boolean isInstalledOrHasDataOnAnyOtherUser(int[] allUsers, int currentUser) {
+    boolean isInstalledOnAnyOtherUser(int[] allUsers, int currentUser) {
         for (int user: allUsers) {
             if (user == currentUser) {
                 continue;
@@ -834,6 +834,16 @@
             if (userState.isInstalled()) {
                 return true;
             }
+        }
+        return false;
+    }
+
+    boolean hasDataOnAnyOtherUser(int[] allUsers, int currentUser) {
+        for (int user: allUsers) {
+            if (user == currentUser) {
+                continue;
+            }
+            final PackageUserStateInternal userState = readUserState(user);
             if (userState.dataExists()) {
                 return true;
             }
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index 2854453..02e28dd 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -355,16 +355,22 @@
     // Called to clean up disabled system packages
     public void removePackageData(final PackageSetting deletedPs, @NonNull int[] allUserHandles) {
         synchronized (mPm.mInstallLock) {
-            removePackageDataLIF(deletedPs, allUserHandles, new PackageRemovedInfo(),
-                    /* flags= */ 0, /* writeSettings= */ false);
+            removePackageDataLIF(deletedPs, UserHandle.USER_ALL, allUserHandles,
+                    new PackageRemovedInfo(), /* flags= */ 0, /* writeSettings= */ false);
         }
     }
 
-    /*
+    /**
      * This method deletes the package from internal data structures such as mPackages / mSettings.
+     *
+     * @param targetUserId indicates the target user of the deletion. It equals to
+     *                     {@link UserHandle.USER_ALL} if the deletion was initiated for all users,
+     *                     otherwise it equals to the specific user id that the deletion was meant
+     *                     for.
      */
     @GuardedBy("mPm.mInstallLock")
-    public void removePackageDataLIF(final PackageSetting deletedPs, @NonNull int[] allUserHandles,
+    public void removePackageDataLIF(final PackageSetting deletedPs, int targetUserId,
+            @NonNull int[] allUserHandles,
             @NonNull PackageRemovedInfo outInfo, int flags, boolean writeSettings) {
         String packageName = deletedPs.getPackageName();
         if (DEBUG_REMOVE) Slog.d(TAG, "removePackageDataLI: " + deletedPs);
@@ -372,7 +378,7 @@
         final AndroidPackage deletedPkg = deletedPs.getPkg();
 
         // Delete all the data and states related to this package.
-        clearPackageStateForUserLIF(deletedPs, UserHandle.USER_ALL, flags);
+        clearPackageStateForUserLIF(deletedPs, targetUserId, flags);
 
         // Delete from mPackages
         removePackageLI(packageName, (flags & PackageManager.DELETE_CHATTY) != 0);
@@ -384,7 +390,7 @@
             deletedPs.setPkg(null);
         }
 
-        if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
+        if (shouldDeletePackageSetting(deletedPs, targetUserId, allUserHandles, flags)) {
             // Delete from mSettings
             final SparseBooleanArray changedUsers = new SparseBooleanArray();
             synchronized (mPm.mLock) {
@@ -457,6 +463,25 @@
         }
     }
 
+    private static boolean shouldDeletePackageSetting(PackageSetting deletedPs, int userId,
+                                                      int[] allUserHandles, int flags) {
+        if ((flags & PackageManager.DELETE_KEEP_DATA) != 0) {
+            return false;
+        }
+        if (userId == UserHandle.USER_ALL) {
+            // Deleting for ALL. Let's wipe the PackageSetting.
+            return true;
+        }
+        if (deletedPs.hasDataOnAnyOtherUser(allUserHandles, userId)) {
+            // We arrived here because we are uninstalling the package for a specified user, and the
+            // package isn't installed on any other user. Before we proceed to completely delete the
+            // PackageSetting from mSettings, let's first check if data exists on any other user.
+            // If so, do not wipe the PackageSetting.
+            return false;
+        }
+        return true;
+    }
+
     void cleanUpResources(@Nullable String packageName, @Nullable File codeFile,
                           @Nullable String[] instructionSets) {
         synchronized (mPm.mInstallLock) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index d683855..5d710d2 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -1405,7 +1405,9 @@
                     case AppOpsManager.MODE_ERRORED: {
                         if (permission.equals(Manifest.permission.BLUETOOTH_CONNECT)) {
                             Slog.e(LOG_TAG, "BLUETOOTH_CONNECT permission hard denied as op"
-                                    + " mode is MODE_ERRORED for " + attributionSource);
+                                    + " mode is MODE_ERRORED. Permission check was requested for: "
+                                    + attributionSource + " and op transaction was invoked for "
+                                    + current);
                         }
                         return PermissionChecker.PERMISSION_HARD_DENIED;
                     }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index b8a92bb..febcc05 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -183,6 +183,8 @@
 import static com.android.server.wm.ActivityRecordProto.REPORTED_DRAWN;
 import static com.android.server.wm.ActivityRecordProto.REPORTED_VISIBLE;
 import static com.android.server.wm.ActivityRecordProto.SHOULD_FORCE_ROTATE_FOR_CAMERA_COMPAT;
+import static com.android.server.wm.ActivityRecordProto.SHOULD_IGNORE_ORIENTATION_REQUEST_LOOP;
+import static com.android.server.wm.ActivityRecordProto.SHOULD_OVERRIDE_FORCE_RESIZE_APP;
 import static com.android.server.wm.ActivityRecordProto.SHOULD_OVERRIDE_MIN_ASPECT_RATIO;
 import static com.android.server.wm.ActivityRecordProto.SHOULD_REFRESH_ACTIVITY_FOR_CAMERA_COMPAT;
 import static com.android.server.wm.ActivityRecordProto.SHOULD_REFRESH_ACTIVITY_VIA_PAUSE_FOR_CAMERA_COMPAT;
@@ -2492,14 +2494,7 @@
 
         ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Creating SnapshotStartingData");
         mStartingData = new SnapshotStartingData(mWmService, snapshot, typeParams);
-        if ((!mStyleFillsParent && task.getChildCount() > 1)
-                || task.forAllLeafTaskFragments(TaskFragment::isEmbedded)) {
-            // Case 1:
-            // If it is moving a Task{[0]=main activity, [1]=translucent activity} to front, use
-            // shared starting window so that the transition doesn't need to wait for the activity
-            // behind the translucent activity. Also, onFirstWindowDrawn will check all visible
-            // activities are drawn in the task to remove the snapshot starting window.
-            // Case 2:
+        if (task.forAllLeafTaskFragments(TaskFragment::isEmbedded)) {
             // Associate with the task so if this activity is resized by task fragment later, the
             // starting window can keep the same bounds as the task.
             associateStartingDataWithTask();
@@ -10337,6 +10332,10 @@
                 mLetterboxUiController.shouldRefreshActivityViaPauseForCameraCompat());
         proto.write(SHOULD_OVERRIDE_MIN_ASPECT_RATIO,
                 mLetterboxUiController.shouldOverrideMinAspectRatio());
+        proto.write(SHOULD_IGNORE_ORIENTATION_REQUEST_LOOP,
+                mLetterboxUiController.shouldIgnoreOrientationRequestLoop());
+        proto.write(SHOULD_OVERRIDE_FORCE_RESIZE_APP,
+                mLetterboxUiController.shouldOverrideForceResizeApp());
     }
 
     @Override
@@ -10622,13 +10621,6 @@
 
     @Override
     boolean isSyncFinished(BLASTSyncEngine.SyncGroup group) {
-        if (task != null && task.mSharedStartingData != null) {
-            final WindowState startingWin = task.topStartingWindow();
-            if (startingWin != null && startingWin.isSyncFinished(group)) {
-                // The sync is ready if a drawn starting window covered the task.
-                return true;
-            }
-        }
         if (!super.isSyncFinished(group)) return false;
         if (mDisplayContent != null && mDisplayContent.mUnknownAppVisibilityController
                 .isVisibilityUnknown(this)) {
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index eed46fe..fc3a338 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -61,6 +61,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.FrameworkStatsLog;
+import com.android.internal.util.Preconditions;
 import com.android.server.UiThread;
 import com.android.server.am.PendingIntentRecord;
 import com.android.window.flags.Flags;
@@ -219,6 +220,9 @@
         private final WindowProcessController mCallerApp;
         private final WindowProcessController mRealCallerApp;
         private final boolean mIsCallForResult;
+        private final ActivityOptions mCheckedOptions;
+        private BalVerdict mResultForCaller;
+        private BalVerdict mResultForRealCaller;
 
         private BalState(int callingUid, int callingPid, final String callingPackage,
                  int realCallingUid, int realCallingPid,
@@ -239,6 +243,7 @@
             mIntent = intent;
             mRealCallingPackage = mService.getPackageNameIfUnique(realCallingUid, realCallingPid);
             mIsCallForResult = resultRecord != null;
+            mCheckedOptions = checkedOptions;
             if (balRequireOptInByPendingIntentCreator() // auto-opt in introduced with this feature
                     && (originatingPendingIntent == null // not a PendingIntent
                     || mIsCallForResult) // sent for result
@@ -369,8 +374,19 @@
             return mCallingUid == mRealCallingUid;
         }
 
-        private String dump(BalVerdict resultIfPiCreatorAllowsBal,
-                            BalVerdict resultIfPiSenderAllowsBal) {
+        public void setResultForCaller(BalVerdict resultForCaller) {
+            Preconditions.checkState(mResultForCaller == null,
+                    "mResultForCaller can only be set once");
+            this.mResultForCaller = resultForCaller;
+        }
+
+        public void setResultForRealCaller(BalVerdict resultForRealCaller) {
+            Preconditions.checkState(mResultForRealCaller == null,
+                    "mResultForRealCaller can only be set once");
+            this.mResultForRealCaller = resultForRealCaller;
+        }
+
+        private String dump() {
             StringBuilder sb = new StringBuilder(2048);
             sb.append("[callingPackage: ")
                     .append(getDebugPackageName(mCallingPackage, mCallingUid));
@@ -392,7 +408,7 @@
             sb.append("; balAllowedByPiCreator: ").append(mBalAllowedByPiCreator);
             sb.append("; balAllowedByPiCreatorWithHardening: ")
                     .append(mBalAllowedByPiCreatorWithHardening);
-            sb.append("; resultIfPiCreatorAllowsBal: ").append(resultIfPiCreatorAllowsBal);
+            sb.append("; resultIfPiCreatorAllowsBal: ").append(mResultForCaller);
             sb.append("; hasRealCaller: ").append(hasRealCaller());
             sb.append("; isCallForResult: ").append(mIsCallForResult);
             sb.append("; isPendingIntent: ").append(isPendingIntent());
@@ -416,7 +432,7 @@
                             .append(mRealCallerApp.hasActivityInVisibleTask());
                 }
                 sb.append("; balAllowedByPiSender: ").append(mBalAllowedByPiSender);
-                sb.append("; resultIfPiSenderAllowsBal: ").append(resultIfPiSenderAllowsBal);
+                sb.append("; resultIfPiSenderAllowsBal: ").append(mResultForRealCaller);
             }
             sb.append("]");
             return sb.toString();
@@ -559,23 +575,25 @@
             // realCallingSdkSandboxUidToAppUid should probably just be used instead (or in addition
             // to realCallingUid when calculating resultForRealCaller below.
             if (mService.hasActiveVisibleWindow(realCallingSdkSandboxUidToAppUid)) {
-                BalVerdict balVerdict = new BalVerdict(BAL_ALLOW_SDK_SANDBOX, /*background*/ false,
-                        "uid in SDK sandbox has visible (non-toast) window");
-                return statsLog(balVerdict, state);
+                state.setResultForRealCaller(new BalVerdict(BAL_ALLOW_SDK_SANDBOX,
+                        /*background*/ false,
+                        "uid in SDK sandbox has visible (non-toast) window"));
+                return allowBasedOnRealCaller(state);
             }
         }
 
         BalVerdict resultForCaller = checkBackgroundActivityStartAllowedByCaller(state);
+        state.setResultForCaller(resultForCaller);
 
         if (!state.hasRealCaller()) {
             if (resultForCaller.allows()) {
                 if (DEBUG_ACTIVITY_STARTS) {
                     Slog.d(TAG, "Background activity start allowed. "
-                            + state.dump(resultForCaller, resultForCaller));
+                            + state.dump());
                 }
-                return statsLog(resultForCaller, state);
+                return allowBasedOnCaller(state);
             }
-            return abortLaunch(state, resultForCaller, resultForCaller);
+            return abortLaunch(state);
         }
 
         // The realCaller result is only calculated for PendingIntents (indicated by a valid
@@ -589,6 +607,8 @@
                 ? resultForCaller
                 : checkBackgroundActivityStartAllowedBySender(state, checkedOptions)
                         .setBasedOnRealCaller();
+        state.setResultForRealCaller(resultForRealCaller);
+
         if (state.isPendingIntent()) {
             resultForCaller.setOnlyCreatorAllows(
                     resultForCaller.allows() && resultForRealCaller.blocks());
@@ -600,18 +620,18 @@
                 == ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED) {
             if (DEBUG_ACTIVITY_STARTS) {
                 Slog.d(TAG, "Activity start explicitly allowed by caller. "
-                        + state.dump(resultForCaller, resultForRealCaller));
+                        + state.dump());
             }
-            return statsLog(resultForCaller, state);
+            return allowBasedOnCaller(state);
         }
         if (resultForRealCaller.allows()
                 && checkedOptions.getPendingIntentBackgroundActivityStartMode()
                 == ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED) {
             if (DEBUG_ACTIVITY_STARTS) {
                 Slog.d(TAG, "Activity start explicitly allowed by real caller. "
-                        + state.dump(resultForCaller, resultForRealCaller));
+                        + state.dump());
             }
-            return statsLog(resultForRealCaller, state);
+            return allowBasedOnRealCaller(state);
         }
         // Handle PendingIntent cases with default behavior next
         boolean callerCanAllow = resultForCaller.allows()
@@ -626,26 +646,24 @@
                 // Will be allowed even with BAL hardening.
                 if (DEBUG_ACTIVITY_STARTS) {
                     Slog.d(TAG, "Activity start allowed by caller. "
-                            + state.dump(resultForCaller, resultForRealCaller));
+                            + state.dump());
                 }
-                // return the realCaller result for backwards compatibility
-                return statsLog(resultForRealCaller, state);
+                return allowBasedOnCaller(state);
             }
             if (state.mBalAllowedByPiCreator.allowsBackgroundActivityStarts()) {
                 Slog.wtf(TAG,
                         "With Android 15 BAL hardening this activity start may be blocked"
                                 + " if the PI creator upgrades target_sdk to 35+"
                                 + " AND the PI sender upgrades target_sdk to 34+! "
-                                + state.dump(resultForCaller, resultForRealCaller));
+                                + state.dump());
                 showBalRiskToast();
-                // return the realCaller result for backwards compatibility
-                return statsLog(resultForRealCaller, state);
+                return allowBasedOnCaller(state);
             }
             Slog.wtf(TAG,
                     "Without Android 15 BAL hardening this activity start would be allowed"
                             + " (missing opt in by PI creator or sender)! "
-                            + state.dump(resultForCaller, resultForRealCaller));
-            return abortLaunch(state, resultForCaller, resultForRealCaller);
+                            + state.dump());
+            return abortLaunch(state);
         }
         if (callerCanAllow) {
             // Allowed before V by creator
@@ -653,24 +671,24 @@
                 // Will be allowed even with BAL hardening.
                 if (DEBUG_ACTIVITY_STARTS) {
                     Slog.d(TAG, "Activity start allowed by caller. "
-                            + state.dump(resultForCaller, resultForRealCaller));
+                            + state.dump());
                 }
-                return statsLog(resultForCaller, state);
+                return allowBasedOnCaller(state);
             }
             if (state.mBalAllowedByPiCreator.allowsBackgroundActivityStarts()) {
                 Slog.wtf(TAG,
                         "With Android 15 BAL hardening this activity start may be blocked"
                                 + " if the PI creator upgrades target_sdk to 35+! "
                                 + " (missing opt in by PI creator)! "
-                                + state.dump(resultForCaller, resultForRealCaller));
+                                + state.dump());
                 showBalRiskToast();
-                return statsLog(resultForCaller, state);
+                return allowBasedOnCaller(state);
             }
             Slog.wtf(TAG,
                     "Without Android 15 BAL hardening this activity start would be allowed"
                             + " (missing opt in by PI creator)! "
-                            + state.dump(resultForCaller, resultForRealCaller));
-            return abortLaunch(state, resultForCaller, resultForRealCaller);
+                            + state.dump());
+            return abortLaunch(state);
         }
         if (realCallerCanAllow) {
             // Allowed before U by sender
@@ -679,23 +697,38 @@
                         "With Android 14 BAL hardening this activity start will be blocked"
                                 + " if the PI sender upgrades target_sdk to 34+! "
                                 + " (missing opt in by PI sender)! "
-                                + state.dump(resultForCaller, resultForRealCaller));
+                                + state.dump());
                 showBalRiskToast();
-                return statsLog(resultForRealCaller, state);
+                return allowBasedOnRealCaller(state);
             }
             Slog.wtf(TAG, "Without Android 14 BAL hardening this activity start would be allowed"
                     + " (missing opt in by PI sender)! "
-                    + state.dump(resultForCaller, resultForRealCaller));
-            return abortLaunch(state, resultForCaller, resultForRealCaller);
+                    + state.dump());
+            return abortLaunch(state);
         }
         // neither the caller not the realCaller can allow or have explicitly opted out
-        return abortLaunch(state, resultForCaller, resultForRealCaller);
+        return abortLaunch(state);
     }
 
-    private BalVerdict abortLaunch(BalState state, BalVerdict resultForCaller,
-            BalVerdict resultForRealCaller) {
+    private BalVerdict allowBasedOnCaller(BalState state) {
+        if (DEBUG_ACTIVITY_STARTS) {
+            Slog.d(TAG, "Background activity launch allowed based on caller. "
+                    + state.dump());
+        }
+        return statsLog(state.mResultForCaller, state);
+    }
+
+    private BalVerdict allowBasedOnRealCaller(BalState state) {
+        if (DEBUG_ACTIVITY_STARTS) {
+            Slog.d(TAG, "Background activity launch allowed based on real caller. "
+                    + state.dump());
+        }
+        return statsLog(state.mResultForRealCaller, state);
+    }
+
+    private BalVerdict abortLaunch(BalState state) {
         Slog.w(TAG, "Background activity launch blocked! "
-                + state.dump(resultForCaller, resultForRealCaller));
+                + state.dump());
         showBalBlockedToast();
         return statsLog(BalVerdict.BLOCK, state);
     }
@@ -1471,24 +1504,36 @@
                 && (callingUid == Process.SYSTEM_UID || realCallingUid == Process.SYSTEM_UID)) {
             String activityName = intent != null
                     ? requireNonNull(intent.getComponent()).flattenToShortString() : "";
-            FrameworkStatsLog.write(FrameworkStatsLog.BAL_ALLOWED,
-                    activityName,
-                    BAL_ALLOW_PENDING_INTENT,
-                    callingUid,
-                    realCallingUid);
+            writeBalAllowedLog(activityName, BAL_ALLOW_PENDING_INTENT,
+                    state);
         }
         if (code == BAL_ALLOW_PERMISSION || code == BAL_ALLOW_FOREGROUND
-                    || code == BAL_ALLOW_SAW_PERMISSION) {
+                || code == BAL_ALLOW_SAW_PERMISSION) {
             // We don't need to know which activity in this case.
-            FrameworkStatsLog.write(FrameworkStatsLog.BAL_ALLOWED,
-                    /*activityName*/ "",
-                    code,
-                    callingUid,
-                    realCallingUid);
+            writeBalAllowedLog("", code, state);
+
         }
         return finalVerdict;
     }
 
+    private static void writeBalAllowedLog(String activityName, int code, BalState state) {
+        FrameworkStatsLog.write(FrameworkStatsLog.BAL_ALLOWED,
+                activityName,
+                code,
+                state.mCallingUid,
+                state.mRealCallingUid,
+                state.mResultForCaller == null ? BAL_BLOCK : state.mResultForCaller.getRawCode(),
+                state.mBalAllowedByPiCreator.allowsBackgroundActivityStarts(),
+                state.mCheckedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
+                        != ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED,
+                state.mResultForRealCaller == null ? BAL_BLOCK
+                        : state.mResultForRealCaller.getRawCode(),
+                state.mBalAllowedByPiSender.allowsBackgroundActivityStarts(),
+                state.mCheckedOptions.getPendingIntentBackgroundActivityStartMode()
+                        != ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED
+        );
+    }
+
     /**
      * Called whenever an activity finishes. Stores the record, so it can be used by ASM grace
      * period checks.
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 68bd326..47972b3 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -513,7 +513,6 @@
      *     timer and activity is not letterboxed for fixed orientation
      * </ul>
      */
-    @VisibleForTesting
     boolean shouldIgnoreOrientationRequestLoop() {
         if (!shouldEnableWithOptInOverrideAndOptOutProperty(
                 /* gatingCondition */ mLetterboxConfiguration
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 3b06343..d556f09 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -1411,13 +1411,12 @@
         return isUidPresent;
     }
 
-    WindowState topStartingWindow() {
-        return getWindow(w -> w.mAttrs.type == TYPE_APPLICATION_STARTING);
-    }
-
     ActivityRecord topActivityContainsStartingWindow() {
-        final WindowState startingWindow = topStartingWindow();
-        return startingWindow != null ? startingWindow.mActivityRecord : null;
+        if (getParent() == null) {
+            return null;
+        }
+        return getActivity((r) -> r.getWindow(window ->
+                window.getBaseType() == TYPE_APPLICATION_STARTING) != null);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 93cce2a..f51bd1b 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.Manifest.permission.MANAGE_ACTIVITY_TASKS;
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
@@ -57,6 +58,7 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TRANSITION;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.ActivityTaskManagerService.checkPermission;
 import static com.android.server.wm.ActivityTaskSupervisor.printThisActivity;
 import static com.android.server.wm.IdentifierProto.HASH_CODE;
 import static com.android.server.wm.IdentifierProto.TITLE;
@@ -80,7 +82,9 @@
 import android.app.servertransaction.NewIntentItem;
 import android.app.servertransaction.PauseActivityItem;
 import android.app.servertransaction.ResumeActivityItem;
+import android.content.PermissionChecker;
 import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -744,7 +748,17 @@
         // The system is trusted to embed other apps securely and for all users.
         return UserHandle.getAppId(uid) == SYSTEM_UID
                 // Activities from the same UID can be embedded freely by the host.
-                || a.isUid(uid);
+                || a.isUid(uid)
+                // Apps which have the signature MANAGE_ACTIVITY_TASK permission are trusted.
+                || hasManageTaskPermission(uid);
+    }
+
+    /**
+     * Checks if a particular app uid has the {@link MANAGE_ACTIVITY_TASKS} permission.
+     */
+    private static boolean hasManageTaskPermission(int uid) {
+        return checkPermission(MANAGE_ACTIVITY_TASKS, PermissionChecker.PID_UNKNOWN, uid)
+                == PackageManager.PERMISSION_GRANTED;
     }
 
     /**
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 9ba0a2a..afb0b20 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -114,6 +114,7 @@
     jmethodID notifyFocusChanged;
     jmethodID notifySensorEvent;
     jmethodID notifySensorAccuracy;
+    jmethodID notifyStickyModifierStateChanged;
     jmethodID notifyStylusGestureStarted;
     jmethodID isInputMethodConnectionActive;
     jmethodID notifyVibratorState;
@@ -270,7 +271,8 @@
 class NativeInputManager : public virtual InputReaderPolicyInterface,
                            public virtual InputDispatcherPolicyInterface,
                            public virtual PointerControllerPolicyInterface,
-                           public virtual PointerChoreographerPolicyInterface {
+                           public virtual PointerChoreographerPolicyInterface,
+                           public virtual InputFilterPolicyInterface {
 protected:
     virtual ~NativeInputManager();
 
@@ -388,6 +390,10 @@
             PointerControllerInterface::ControllerType type) override;
     void notifyPointerDisplayIdChanged(int32_t displayId, const FloatPoint& position) override;
 
+    /* --- InputFilterPolicyInterface implementation --- */
+    void notifyStickyModifierStateChanged(uint32_t modifierState,
+                                          uint32_t lockedModifierState) override;
+
 private:
     sp<InputManagerInterface> mInputManager;
 
@@ -477,7 +483,7 @@
 
     mServiceObj = env->NewGlobalRef(serviceObj);
 
-    InputManager* im = new InputManager(this, *this, *this);
+    InputManager* im = new InputManager(this, *this, *this, *this);
     mInputManager = im;
     defaultServiceManager()->addService(String16("inputflinger"), im);
 }
@@ -806,6 +812,14 @@
     checkAndClearExceptionFromCallback(env, "onPointerDisplayIdChanged");
 }
 
+void NativeInputManager::notifyStickyModifierStateChanged(uint32_t modifierState,
+                                                          uint32_t lockedModifierState) {
+    JNIEnv* env = jniEnv();
+    env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyStickyModifierStateChanged,
+                        modifierState, lockedModifierState);
+    checkAndClearExceptionFromCallback(env, "notifyStickyModifierStateChanged");
+}
+
 sp<SurfaceControl> NativeInputManager::getParentSurfaceForPointers(int displayId) {
     JNIEnv* env = jniEnv();
     jlong nativeSurfaceControlPtr =
@@ -2957,6 +2971,9 @@
     GET_METHOD_ID(gServiceClassInfo.onPointerDisplayIdChanged, clazz, "onPointerDisplayIdChanged",
                   "(IFF)V");
 
+    GET_METHOD_ID(gServiceClassInfo.notifyStickyModifierStateChanged, clazz,
+                  "notifyStickyModifierStateChanged", "(II)V");
+
     GET_METHOD_ID(gServiceClassInfo.onPointerDownOutsideFocus, clazz,
             "onPointerDownOutsideFocus", "(Landroid/os/IBinder;)V");
 
diff --git a/services/core/jni/gnss/Android.bp b/services/core/jni/gnss/Android.bp
index f3ba484f62..e72259f 100644
--- a/services/core/jni/gnss/Android.bp
+++ b/services/core/jni/gnss/Android.bp
@@ -68,5 +68,6 @@
         "android.hardware.gnss@2.1",
         "android.hardware.gnss.measurement_corrections@1.0",
         "android.hardware.gnss.visibility_control@1.0",
+        "android_location_flags_c_lib",
     ],
 }
diff --git a/services/core/jni/gnss/Utils.cpp b/services/core/jni/gnss/Utils.cpp
index 571534f..274e3b7 100644
--- a/services/core/jni/gnss/Utils.cpp
+++ b/services/core/jni/gnss/Utils.cpp
@@ -20,6 +20,7 @@
 
 #include <android/hardware/gnss/1.0/IGnss.h>
 #include <android/hardware/gnss/2.0/IGnss.h>
+#include <android_location_flags.h>
 #include <utils/SystemClock.h>
 /*
  * Save a pointer to JavaVm to attach/detach threads executing
@@ -27,6 +28,8 @@
  */
 JavaVM* android::ScopedJniThreadAttach::sJvm;
 
+namespace location_flags = android::location::flags;
+
 namespace android {
 
 namespace {
@@ -194,7 +197,12 @@
 
     flags = static_cast<uint32_t>(location.elapsedRealtime.flags);
     if (flags & android::hardware::gnss::ElapsedRealtime::HAS_TIMESTAMP_NS) {
-        SET(ElapsedRealtimeNanos, location.elapsedRealtime.timestampNs);
+        if (location_flags::replace_future_elapsed_realtime_jni() &&
+            location.elapsedRealtime.timestampNs > android::elapsedRealtimeNano()) {
+            SET(ElapsedRealtimeNanos, android::elapsedRealtimeNano());
+        } else {
+            SET(ElapsedRealtimeNanos, location.elapsedRealtime.timestampNs);
+        }
     } else {
         SET(ElapsedRealtimeNanos, android::elapsedRealtimeNano());
     }
diff --git a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
index 6899ad4..31409ab 100644
--- a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
@@ -109,7 +109,7 @@
                             PermissionUtils.hasPermission(mContext, mClientAppInfo.getPackageName(),
                                     Manifest.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS),
                             /*defaultProviderId=*/flattenedPrimaryProviders),
-                    providerDataList);
+                    providerDataList, /*isRequestForAllOptions=*/ false);
             mClientCallback.onPendingIntent(mPendingIntent);
         } catch (RemoteException e) {
             mRequestSessionMetric.collectUiReturnedFinalPhase(/*uiReturned=*/ false);
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
index 3c190bf..f092dcc 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
@@ -150,9 +150,12 @@
      *
      * @param requestInfo      the information about the request
      * @param providerDataList the list of provider data from remote providers
+     * @param isRequestForAllOptions whether the bottom sheet should directly navigate to the
+     *                               all options page
      */
     public PendingIntent createPendingIntent(
-            RequestInfo requestInfo, ArrayList<ProviderData> providerDataList) {
+            RequestInfo requestInfo, ArrayList<ProviderData> providerDataList,
+            boolean isRequestForAllOptions) {
         List<CredentialProviderInfo> allProviders =
                 CredentialProviderInfoFactory.getCredentialProviderServices(
                         mContext,
@@ -168,7 +171,8 @@
                         disabledProvider.getComponentName().flattenToString())).toList();
 
         Intent intent = IntentFactory.createCredentialSelectorIntent(requestInfo, providerDataList,
-                        new ArrayList<>(disabledProviderDataList), mResultReceiver)
+                        new ArrayList<>(disabledProviderDataList), mResultReceiver,
+                        isRequestForAllOptions)
                 .setAction(UUID.randomUUID().toString());
         //TODO: Create unique pending intent using request code and cancel any pre-existing pending
         // intents
diff --git a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
index ca5600e..d165171 100644
--- a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
@@ -106,7 +106,8 @@
                         mRequestId, mClientRequest, mClientAppInfo.getPackageName(),
                         PermissionUtils.hasPermission(mContext, mClientAppInfo.getPackageName(),
                                 Manifest.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS)),
-                providerDataList);
+                providerDataList,
+                /*isRequestForAllOptions=*/ true);
 
         List<GetCredentialProviderData> candidateProviderDataList = new ArrayList<>();
         for (ProviderData providerData : providerDataList) {
diff --git a/services/credentials/java/com/android/server/credentials/GetRequestSession.java b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
index c9e691e..3f57c80 100644
--- a/services/credentials/java/com/android/server/credentials/GetRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
@@ -99,21 +99,24 @@
     protected void launchUiWithProviderData(ArrayList<ProviderData> providerDataList) {
         mRequestSessionMetric.collectUiCallStartTime(System.nanoTime());
         mCredentialManagerUi.setStatus(CredentialManagerUi.UiStatus.USER_INTERACTION);
-        Binder.withCleanCallingIdentity(()-> {
-        try {
+        Binder.withCleanCallingIdentity(() -> {
+            try {
                 cancelExistingPendingIntent();
-            mPendingIntent = mCredentialManagerUi.createPendingIntent(
-                    RequestInfo.newGetRequestInfo(
-                            mRequestId, mClientRequest, mClientAppInfo.getPackageName(),
-                            PermissionUtils.hasPermission(mContext, mClientAppInfo.getPackageName(),
-                                    Manifest.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS)),
-                    providerDataList);
-            mClientCallback.onPendingIntent(mPendingIntent);
-        } catch (RemoteException e) {
-            mRequestSessionMetric.collectUiReturnedFinalPhase(/*uiReturned=*/ false);
-            mCredentialManagerUi.setStatus(CredentialManagerUi.UiStatus.TERMINATED);
-            String exception = GetCredentialException.TYPE_UNKNOWN;
-            mRequestSessionMetric.collectFrameworkException(exception);
+                mPendingIntent = mCredentialManagerUi.createPendingIntent(
+                        RequestInfo.newGetRequestInfo(
+                                mRequestId, mClientRequest, mClientAppInfo.getPackageName(),
+                                PermissionUtils.hasPermission(mContext,
+                                        mClientAppInfo.getPackageName(),
+                                        Manifest.permission
+                                                .CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS)),
+                        providerDataList,
+                        /*isRequestForAllOptions=*/ false);
+                mClientCallback.onPendingIntent(mPendingIntent);
+            } catch (RemoteException e) {
+                mRequestSessionMetric.collectUiReturnedFinalPhase(/*uiReturned=*/ false);
+                mCredentialManagerUi.setStatus(CredentialManagerUi.UiStatus.TERMINATED);
+                String exception = GetCredentialException.TYPE_UNKNOWN;
+                mRequestSessionMetric.collectFrameworkException(exception);
                 respondToClientWithErrorAndFinish(exception, "Unable to instantiate selector");
             }
         });
diff --git a/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java b/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java
index f447c1f..fbfc9ca 100644
--- a/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java
@@ -192,7 +192,7 @@
                             mRequestId, mClientRequest, mClientAppInfo.getPackageName(),
                             PermissionUtils.hasPermission(mContext, mClientAppInfo.getPackageName(),
                                     Manifest.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS)),
-                    providerDataList);
+                    providerDataList, /*isRequestForAllOptions=*/ false);
         } else {
             return null;
         }
diff --git a/services/print/Android.bp b/services/print/Android.bp
index 5b4349a..0dfceaa 100644
--- a/services/print/Android.bp
+++ b/services/print/Android.bp
@@ -19,4 +19,7 @@
     defaults: ["platform_service_defaults"],
     srcs: [":services.print-sources"],
     libs: ["services.core"],
+    lint: {
+        baseline_filename: "lint-baseline.xml",
+    },
 }
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 7a84406..e370f55 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -759,6 +759,15 @@
                         AUTO_BRIGHTNESS_MODE_DEFAULT,
                         Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_DIM), SMALL_DELTA);
 
+        assertArrayEquals(new float[]{0.0f, 80},
+                mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(
+                        AUTO_BRIGHTNESS_MODE_DEFAULT,
+                        Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_BRIGHT), ZERO_DELTA);
+        assertArrayEquals(new float[]{0.6f, 0.7f},
+                mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels(
+                        AUTO_BRIGHTNESS_MODE_DEFAULT,
+                        Settings.System.SCREEN_BRIGHTNESS_AUTOMATIC_BRIGHT), SMALL_DELTA);
+
         assertArrayEquals(new float[]{0.0f, 95},
                 mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(
                         AUTO_BRIGHTNESS_MODE_DOZE,
@@ -1197,6 +1206,20 @@
                 +           "</map>\n"
                 +       "</luxToBrightnessMapping>\n"
                 +       "<luxToBrightnessMapping>\n"
+                +           "<mode>default</mode>\n"
+                +           "<setting>bright</setting>\n"
+                +           "<map>\n"
+                +               "<point>\n"
+                +                   "<first>0</first>\n"
+                +                   "<second>0.6</second>\n"
+                +               "</point>\n"
+                +               "<point>\n"
+                +                   "<first>80</first>\n"
+                +                   "<second>0.7</second>\n"
+                +               "</point>\n"
+                +           "</map>\n"
+                +       "</luxToBrightnessMapping>\n"
+                +       "<luxToBrightnessMapping>\n"
                 +           "<mode>doze</mode>\n"
                 +           "<map>\n"
                 +               "<point>\n"
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
index efcdbd4..1cd61e9 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
@@ -44,10 +44,6 @@
 import android.graphics.PointF;
 import android.os.Looper;
 import android.os.SystemClock;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.testing.DexmakerShareClassLoaderRule;
 import android.view.InputDevice;
 import android.view.MotionEvent;
@@ -60,7 +56,6 @@
 import com.android.server.accessibility.AccessibilityManagerService;
 import com.android.server.accessibility.AccessibilityTraceManager;
 import com.android.server.accessibility.EventStreamTransformation;
-import com.android.server.accessibility.Flags;
 import com.android.server.accessibility.utils.GestureLogParser;
 import com.android.server.testutils.OffsettableClock;
 
@@ -81,7 +76,6 @@
 import java.util.ArrayList;
 import java.util.List;
 
-
 @RunWith(AndroidJUnit4.class)
 public class TouchExplorerTest {
 
@@ -125,9 +119,6 @@
     public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
             new DexmakerShareClassLoaderRule();
 
-    @Rule
-    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
     /**
      * {@link TouchExplorer#sendDownForAllNotInjectedPointers} injecting events with the same object
      * is resulting {@link ArgumentCaptor} to capture events with last state. Before implementation
@@ -170,16 +161,11 @@
         goFromStateClearTo(STATE_TOUCH_EXPLORING_1FINGER);
         // Wait for transiting to touch exploring state.
         mHandler.fastForward(2 * USER_INTENT_TIMEOUT);
-        assertState(STATE_TOUCH_EXPLORING);
-        // Manually construct the next move event. Using moveEachPointers() will batch the move
-        // event which produces zero movement for some reason.
-        float[] x = new float[1];
-        float[] y = new float[1];
-        x[0] = mLastEvent.getX(0) + mTouchSlop;
-        y[0] = mLastEvent.getY(0) + mTouchSlop;
-        send(manyPointerEvent(ACTION_MOVE, x, y));
+        moveEachPointers(mLastEvent, p(10, 10));
+        send(mLastEvent);
         goToStateClearFrom(STATE_TOUCH_EXPLORING_1FINGER);
         assertCapturedEvents(ACTION_HOVER_ENTER, ACTION_HOVER_MOVE, ACTION_HOVER_EXIT);
+        assertState(STATE_TOUCH_EXPLORING);
     }
 
     /**
@@ -187,8 +173,7 @@
      * change the coordinates.
      */
     @Test
-    @RequiresFlagsEnabled(Flags.FLAG_REDUCE_TOUCH_EXPLORATION_SENSITIVITY)
-    public void testOneFingerMoveWithExtraMoveEvents_generatesOneMoveEvent() {
+    public void testOneFingerMoveWithExtraMoveEvents() {
         goFromStateClearTo(STATE_TOUCH_EXPLORING_1FINGER);
         // Inject a set of move events that have the same coordinates as the down event.
         moveEachPointers(mLastEvent, p(0, 0));
@@ -196,33 +181,7 @@
         // Wait for transition to touch exploring state.
         mHandler.fastForward(2 * USER_INTENT_TIMEOUT);
         // Now move for real.
-        moveAtLeastTouchSlop(mLastEvent);
-        send(mLastEvent);
-        // One more move event with no change.
-        moveEachPointers(mLastEvent, p(0, 0));
-        send(mLastEvent);
-        goToStateClearFrom(STATE_TOUCH_EXPLORING_1FINGER);
-        assertCapturedEvents(
-                ACTION_HOVER_ENTER,
-                ACTION_HOVER_MOVE,
-                ACTION_HOVER_EXIT);
-    }
-
-    /**
-     * Test the case where ACTION_DOWN is followed by a number of ACTION_MOVE events that do not
-     * change the coordinates.
-     */
-    @Test
-    @RequiresFlagsDisabled(Flags.FLAG_REDUCE_TOUCH_EXPLORATION_SENSITIVITY)
-    public void testOneFingerMoveWithExtraMoveEvents_generatesThreeMoveEvent() {
-        goFromStateClearTo(STATE_TOUCH_EXPLORING_1FINGER);
-        // Inject a set of move events that have the same coordinates as the down event.
-        moveEachPointers(mLastEvent, p(0, 0));
-        send(mLastEvent);
-        // Wait for transition to touch exploring state.
-        mHandler.fastForward(2 * USER_INTENT_TIMEOUT);
-        // Now move for real.
-        moveAtLeastTouchSlop(mLastEvent);
+        moveEachPointers(mLastEvent, p(10, 10));
         send(mLastEvent);
         // One more move event with no change.
         moveEachPointers(mLastEvent, p(0, 0));
@@ -283,7 +242,7 @@
         moveEachPointers(mLastEvent, p(0, 0), p(0, 0));
         send(mLastEvent);
         // Now move for real.
-        moveEachPointers(mLastEvent, p(mTouchSlop, mTouchSlop), p(mTouchSlop, mTouchSlop));
+        moveEachPointers(mLastEvent, p(10, 10), p(10, 10));
         send(mLastEvent);
         goToStateClearFrom(STATE_DRAGGING_2FINGERS);
         assertCapturedEvents(ACTION_DOWN, ACTION_MOVE, ACTION_MOVE, ACTION_MOVE, ACTION_UP);
@@ -292,7 +251,7 @@
     @Test
     public void testUpEvent_OneFingerMove_clearStateAndInjectHoverEvents() {
         goFromStateClearTo(STATE_TOUCH_EXPLORING_1FINGER);
-        moveAtLeastTouchSlop(mLastEvent);
+        moveEachPointers(mLastEvent, p(10, 10));
         send(mLastEvent);
         // Wait 10 ms to make sure that hover enter and exit are not scheduled for the same moment.
         mHandler.fastForward(10);
@@ -318,7 +277,7 @@
 
         // Wait for the finger moving to the second view.
         mHandler.fastForward(oneThirdUserIntentTimeout);
-        moveAtLeastTouchSlop(mLastEvent);
+        moveEachPointers(mLastEvent, p(10, 10));
         send(mLastEvent);
 
         // Wait for the finger lifting from the second view.
@@ -443,6 +402,7 @@
         // Manually construct the next move event. Using moveEachPointers() will batch the move
         // event onto the pointer up event which will mean that the move event still has a pointer
         // count of 3.
+        // Todo: refactor to avoid using batching as there is no special reason to do it that way.
         float[] x = new float[2];
         float[] y = new float[2];
         x[0] = mLastEvent.getX(0) + 100;
@@ -774,9 +734,6 @@
         }
     }
 
-    private void moveAtLeastTouchSlop(MotionEvent event) {
-        moveEachPointers(event, p(2 * mTouchSlop, 0));
-    }
     /**
      * A {@link android.os.Handler} that doesn't process messages until {@link #fastForward(int)} is
      * invoked.
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenAdaptersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenAdaptersTest.java
index 6cc1c43..08af09c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenAdaptersTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenAdaptersTest.java
@@ -20,7 +20,11 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.app.Flags;
 import android.app.NotificationManager.Policy;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.service.notification.ZenPolicy;
 
 import androidx.test.filters.SmallTest;
@@ -28,6 +32,7 @@
 
 import com.android.server.UiServiceTestCase;
 
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -35,6 +40,9 @@
 @RunWith(AndroidJUnit4.class)
 public class ZenAdaptersTest extends UiServiceTestCase {
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     @Test
     public void notificationPolicyToZenPolicy_allCallers() {
         Policy policy = new Policy(Policy.PRIORITY_CATEGORY_CALLS, Policy.PRIORITY_SENDERS_ANY, 0);
@@ -127,4 +135,35 @@
         assertThat(zenPolicy.getVisualEffectPeek()).isEqualTo(ZenPolicy.STATE_UNSET);
         assertThat(zenPolicy.getVisualEffectStatusBar()).isEqualTo(ZenPolicy.STATE_UNSET);
     }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void notificationPolicyToZenPolicy_modesApi_priorityChannels() {
+        Policy policy = new Policy(0, 0, 0, 0,
+                Policy.policyState(false, true), 0);
+
+        ZenPolicy zenPolicy = notificationPolicyToZenPolicy(policy);
+        assertThat(zenPolicy.getAllowedChannels()).isEqualTo(ZenPolicy.CHANNEL_TYPE_PRIORITY);
+
+        Policy notAllowed = new Policy(0, 0, 0, 0,
+                Policy.policyState(false, false), 0);
+        ZenPolicy zenPolicyNotAllowed = notificationPolicyToZenPolicy(notAllowed);
+        assertThat(zenPolicyNotAllowed.getAllowedChannels()).isEqualTo(ZenPolicy.CHANNEL_TYPE_NONE);
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_MODES_API)
+    public void notificationPolicyToZenPolicy_noModesApi_priorityChannelsUnset() {
+        Policy policy = new Policy(0, 0, 0, 0,
+                Policy.policyState(false, true), 0);
+
+        ZenPolicy zenPolicy = notificationPolicyToZenPolicy(policy);
+        assertThat(zenPolicy.getAllowedChannels()).isEqualTo(ZenPolicy.CHANNEL_TYPE_UNSET);
+
+        Policy notAllowed = new Policy(0, 0, 0, 0,
+                Policy.policyState(false, false), 0);
+        ZenPolicy zenPolicyNotAllowed = notificationPolicyToZenPolicy(notAllowed);
+        assertThat(zenPolicyNotAllowed.getAllowedChannels())
+                .isEqualTo(ZenPolicy.CHANNEL_TYPE_UNSET);
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 25c0cd9..f84d8e9 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -3876,6 +3876,7 @@
                 .allowCalls(PEOPLE_TYPE_CONTACTS)
                 .allowConversations(CONVERSATION_SENDERS_IMPORTANT)
                 .hideAllVisualEffects()
+                .allowChannels(ZenPolicy.CHANNEL_TYPE_PRIORITY)
                 .build();
         assertThat(mZenModeHelper.mConfig.automaticRules.values())
                 .comparingElementsUsing(IGNORE_TIMESTAMPS)
@@ -3907,6 +3908,7 @@
                 .allowCalls(PEOPLE_TYPE_STARRED)
                 .allowConversations(CONVERSATION_SENDERS_IMPORTANT)
                 .hideAllVisualEffects()
+                .allowChannels(ZenPolicy.CHANNEL_TYPE_PRIORITY)
                 .build();
         assertThat(mZenModeHelper.mConfig.automaticRules.values())
                 .comparingElementsUsing(IGNORE_TIMESTAMPS)
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 8a9c05d..c82f751 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -88,6 +88,7 @@
 import android.app.ActivityOptions;
 import android.app.AppOpsManager;
 import android.app.BackgroundStartPrivileges;
+import android.app.ComponentOptions.BackgroundActivityStartMode;
 import android.app.IApplicationThread;
 import android.app.PictureInPictureParams;
 import android.content.ComponentName;
@@ -914,24 +915,78 @@
                 .mockStatic(FrameworkStatsLog.class)
                 .strictness(Strictness.LENIENT)
                 .startMocking();
-        doReturn(PERMISSION_GRANTED).when(() -> ActivityTaskManagerService.checkPermission(
-                eq(START_ACTIVITIES_FROM_BACKGROUND),
-                anyInt(), anyInt()));
-        runAndVerifyBackgroundActivityStartsSubtest(
-                "allowed_notAborted", false,
-                UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP,
-                UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP,
-                false, true, false, false, false, false, false, false);
-        verify(() -> FrameworkStatsLog.write(FrameworkStatsLog.BAL_ALLOWED,
-                "",  // activity name
-                BackgroundActivityStartController.BAL_ALLOW_PERMISSION,
-                UNIMPORTANT_UID,
-                UNIMPORTANT_UID2));
-        mockingSession.finishMocking();
+        try {
+            doReturn(PERMISSION_GRANTED).when(() -> ActivityTaskManagerService.checkPermission(
+                    eq(START_ACTIVITIES_FROM_BACKGROUND),
+                    anyInt(), anyInt()));
+            runAndVerifyBackgroundActivityStartsSubtest(
+                    "allowed_notAborted", false,
+                    UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP,
+                    UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP,
+                    false, true, false, false, false, false, false, false);
+            verify(() -> FrameworkStatsLog.write(FrameworkStatsLog.BAL_ALLOWED,
+                    "",  // activity name
+                    BackgroundActivityStartController.BAL_ALLOW_PERMISSION,
+                    UNIMPORTANT_UID,
+                    UNIMPORTANT_UID2,
+                    BackgroundActivityStartController.BAL_ALLOW_PERMISSION,
+                    true, // opt in
+                    false, // but no explicit opt in
+                    BackgroundActivityStartController.BAL_BLOCK,
+                    true, // opt in
+                    false // but no explicit opt in
+            ));
+        } finally {
+            mockingSession.finishMocking();
+        }
     }
 
     /**
-     * This test ensures proper logging for BAL_ALLOW_PENDING_INTENT.
+     * This test ensures proper logging for BAL_ALLOW_PENDING_INTENT, when the PendingIntent sender
+     * is the only reason BAL is allowed.
+     */
+    @Test
+    public void testBackgroundActivityStartsAllowed_loggingOnlyPendingIntentAllowed() {
+        doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled();
+        MockitoSession mockingSession = mockitoSession()
+                .mockStatic(ActivityTaskManagerService.class)
+                .mockStatic(FrameworkStatsLog.class)
+                .mockStatic(PendingIntentRecord.class)
+                .strictness(Strictness.LENIENT)
+                .startMocking();
+        try {
+            doReturn(PERMISSION_GRANTED).when(() -> ActivityTaskManagerService.checkPermission(
+                    eq(START_ACTIVITIES_FROM_BACKGROUND),
+                    anyInt(), anyInt()));
+            doReturn(BackgroundStartPrivileges.allowBackgroundActivityStarts(null)).when(
+                    () -> PendingIntentRecord.getBackgroundStartPrivilegesAllowedByCaller(
+                            anyObject(), anyInt(), anyObject()));
+            runAndVerifyBackgroundActivityStartsSubtest(
+                    "allowed_notAborted", false,
+                    UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP,
+                    Process.SYSTEM_UID, true, PROCESS_STATE_BOUND_TOP,
+                    false, true, false, false, false, false, false, false,
+                    ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED);
+            verify(() -> FrameworkStatsLog.write(FrameworkStatsLog.BAL_ALLOWED,
+                    DEFAULT_COMPONENT_PACKAGE_NAME + "/" + DEFAULT_COMPONENT_PACKAGE_NAME,
+                    BackgroundActivityStartController.BAL_ALLOW_PENDING_INTENT,
+                    UNIMPORTANT_UID,
+                    Process.SYSTEM_UID,
+                    BackgroundActivityStartController.BAL_ALLOW_PERMISSION,
+                    false, // opt in
+                    true, // explicit opt out
+                    BackgroundActivityStartController.BAL_ALLOW_VISIBLE_WINDOW,
+                    true, // opt in
+                    false // but no explicit opt in
+            ));
+        } finally {
+            mockingSession.finishMocking();
+        }
+    }
+
+    /**
+     * This test ensures proper logging for BAL_ALLOW_PENDING_INTENT, when the PendingIntent sender
+     * is not the primary reason to allow BAL (but the creator).
      */
     @Test
     public void testBackgroundActivityStartsAllowed_loggingPendingIntentAllowed() {
@@ -942,23 +997,34 @@
                 .mockStatic(PendingIntentRecord.class)
                 .strictness(Strictness.LENIENT)
                 .startMocking();
-        doReturn(PERMISSION_GRANTED).when(() -> ActivityTaskManagerService.checkPermission(
-                eq(START_ACTIVITIES_FROM_BACKGROUND),
-                anyInt(), anyInt()));
-        doReturn(BackgroundStartPrivileges.allowBackgroundActivityStarts(null)).when(
-                () -> PendingIntentRecord.getBackgroundStartPrivilegesAllowedByCaller(
-                anyObject(), anyInt(), anyObject()));
-        runAndVerifyBackgroundActivityStartsSubtest(
-                "allowed_notAborted", false,
-                UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP,
-                Process.SYSTEM_UID, true, PROCESS_STATE_BOUND_TOP,
-                false, true, false, false, false, false, false, false);
-        verify(() -> FrameworkStatsLog.write(FrameworkStatsLog.BAL_ALLOWED,
-                DEFAULT_COMPONENT_PACKAGE_NAME + "/" + DEFAULT_COMPONENT_PACKAGE_NAME,
-                BackgroundActivityStartController.BAL_ALLOW_PENDING_INTENT,
-                UNIMPORTANT_UID,
-                Process.SYSTEM_UID));
-        mockingSession.finishMocking();
+        try {
+            doReturn(PERMISSION_GRANTED).when(() -> ActivityTaskManagerService.checkPermission(
+                    eq(START_ACTIVITIES_FROM_BACKGROUND),
+                    anyInt(), anyInt()));
+            doReturn(BackgroundStartPrivileges.allowBackgroundActivityStarts(null)).when(
+                    () -> PendingIntentRecord.getBackgroundStartPrivilegesAllowedByCaller(
+                            anyObject(), anyInt(), anyObject()));
+            runAndVerifyBackgroundActivityStartsSubtest(
+                    "allowed_notAborted", false,
+                    UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP,
+                    Process.SYSTEM_UID, true, PROCESS_STATE_BOUND_TOP,
+                    false, true, false, false, false, false, false, false,
+                    ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
+            verify(() -> FrameworkStatsLog.write(FrameworkStatsLog.BAL_ALLOWED,
+                    "",
+                    BackgroundActivityStartController.BAL_ALLOW_PERMISSION,
+                    UNIMPORTANT_UID,
+                    Process.SYSTEM_UID,
+                    BackgroundActivityStartController.BAL_ALLOW_PERMISSION,
+                    true, // opt in
+                    true, // explicit opt in
+                    BackgroundActivityStartController.BAL_ALLOW_VISIBLE_WINDOW,
+                    true, // opt in
+                    false // but no explicit opt in
+            ));
+        } finally {
+            mockingSession.finishMocking();
+        }
     }
 
     private void runAndVerifyBackgroundActivityStartsSubtest(String name, boolean shouldHaveAborted,
@@ -971,6 +1037,27 @@
             boolean isCallingUidAffiliatedProfileOwner,
             boolean isPinnedSingleInstance,
             boolean hasSystemExemptAppOp) {
+        runAndVerifyBackgroundActivityStartsSubtest(name, shouldHaveAborted, callingUid,
+                callingUidHasVisibleWindow, callingUidProcState, realCallingUid,
+                realCallingUidHasVisibleWindow,  realCallingUidProcState, hasForegroundActivities,
+                callerIsRecents, callerIsTempAllowed,
+                callerIsInstrumentingWithBackgroundActivityStartPrivileges,
+                isCallingUidDeviceOwner, isCallingUidAffiliatedProfileOwner, isPinnedSingleInstance,
+                hasSystemExemptAppOp,
+                ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED);
+    }
+
+    private void runAndVerifyBackgroundActivityStartsSubtest(String name, boolean shouldHaveAborted,
+            int callingUid, boolean callingUidHasVisibleWindow, int callingUidProcState,
+            int realCallingUid, boolean realCallingUidHasVisibleWindow, int realCallingUidProcState,
+            boolean hasForegroundActivities, boolean callerIsRecents,
+            boolean callerIsTempAllowed,
+            boolean callerIsInstrumentingWithBackgroundActivityStartPrivileges,
+            boolean isCallingUidDeviceOwner,
+            boolean isCallingUidAffiliatedProfileOwner,
+            boolean isPinnedSingleInstance,
+            boolean hasSystemExemptAppOp,
+            @BackgroundActivityStartMode int pendingIntentCreatorBackgroundActivityStartMode) {
         // window visibility
         doReturn(callingUidHasVisibleWindow).when(mAtm).hasActiveVisibleWindow(callingUid);
         doReturn(realCallingUidHasVisibleWindow).when(mAtm).hasActiveVisibleWindow(realCallingUid);
@@ -1022,7 +1109,10 @@
             launchMode = LAUNCH_SINGLE_INSTANCE;
         }
 
-        final ActivityOptions options = spy(ActivityOptions.makeBasic());
+        ActivityOptions rawOptions = ActivityOptions.makeBasic()
+                .setPendingIntentCreatorBackgroundActivityStartMode(
+                        pendingIntentCreatorBackgroundActivityStartMode);
+        final ActivityOptions options = spy(rawOptions);
         ActivityRecord[] outActivity = new ActivityRecord[1];
         ActivityStarter starter = prepareStarter(
                 FLAG_ACTIVITY_NEW_TASK, true, launchMode)
diff --git a/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java b/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
index 0f1e4d1..810cbe8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -138,25 +137,6 @@
     }
 
     @Test
-    public void testFinishSyncByStartingWindow() {
-        final ActivityRecord taskRoot = new ActivityBuilder(mAtm).setCreateTask(true).build();
-        final Task task = taskRoot.getTask();
-        final ActivityRecord translucentTop = new ActivityBuilder(mAtm).setTask(task)
-                .setActivityTheme(android.R.style.Theme_Translucent).build();
-        createWindow(null, TYPE_BASE_APPLICATION, taskRoot, "win");
-        final WindowState startingWindow = createWindow(null, TYPE_APPLICATION_STARTING,
-                translucentTop, "starting");
-        startingWindow.mStartingData = new SnapshotStartingData(mWm, null, 0);
-        task.mSharedStartingData = startingWindow.mStartingData;
-        task.prepareSync();
-
-        final BLASTSyncEngine.SyncGroup group = mock(BLASTSyncEngine.SyncGroup.class);
-        assertFalse(task.isSyncFinished(group));
-        startingWindow.onSyncFinishedDrawing();
-        assertTrue(task.isSyncFinished(group));
-    }
-
-    @Test
     public void testInvisibleSyncCallback() {
         TestWindowContainer mockWC = new TestWindowContainer(mWm, true /* waiter */);
 
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index df349f8..c958aba 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -563,7 +563,10 @@
      *  raw pdu of the status report is in the extended data ("pdu").
      *
      * @throws IllegalArgumentException if destinationAddress or text are empty
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public void sendTextMessage(
             String destinationAddress, String scAddress, String text,
             PendingIntent sentIntent, PendingIntent deliveryIntent) {
@@ -581,8 +584,11 @@
      * Used for logging and diagnostics purposes. The id may be 0.
      *
      * @throws IllegalArgumentException if destinationAddress or text are empty
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      *
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public void sendTextMessage(
             @NonNull String destinationAddress, @Nullable String scAddress, @NonNull String text,
             @Nullable PendingIntent sentIntent, @Nullable PendingIntent deliveryIntent,
@@ -788,12 +794,16 @@
      * </p>
      *
      * @see #sendTextMessage(String, String, String, PendingIntent, PendingIntent)
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(allOf = {
             android.Manifest.permission.MODIFY_PHONE_STATE,
             android.Manifest.permission.SEND_SMS
     })
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public void sendTextMessageWithoutPersisting(
             String destinationAddress, String scAddress, String text,
             PendingIntent sentIntent, PendingIntent deliveryIntent) {
@@ -908,7 +918,10 @@
      *  {@link #RESULT_REMOTE_EXCEPTION} for error.
      *
      * @throws IllegalArgumentException if the format is invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public void injectSmsPdu(
             byte[] pdu, @SmsMessage.Format String format, PendingIntent receivedIntent) {
         if (!format.equals(SmsMessage.FORMAT_3GPP) && !format.equals(SmsMessage.FORMAT_3GPP2)) {
@@ -940,6 +953,7 @@
      * @return an <code>ArrayList</code> of strings that, in order, comprise the original message.
      * @throws IllegalArgumentException if text is null.
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public ArrayList<String> divideMessage(String text) {
         if (null == text) {
             throw new IllegalArgumentException("text is null");
@@ -1046,7 +1060,10 @@
      *  extended data ("pdu").
      *
      * @throws IllegalArgumentException if destinationAddress or data are empty
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public void sendMultipartTextMessage(
             String destinationAddress, String scAddress, ArrayList<String> parts,
             ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) {
@@ -1062,8 +1079,10 @@
      * Used for logging and diagnostics purposes. The id may be 0.
      *
      * @throws IllegalArgumentException if destinationAddress or data are empty
-     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public void sendMultipartTextMessage(
             @NonNull String destinationAddress, @Nullable String scAddress,
             @NonNull List<String> parts, @Nullable List<PendingIntent> sentIntents,
@@ -1089,7 +1108,11 @@
      *
      * @param packageName serves as the default package name if the package name that is
      *        associated with the user id is null.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public void sendMultipartTextMessage(
             @NonNull String destinationAddress, @Nullable String scAddress,
             @NonNull List<String> parts, @Nullable List<PendingIntent> sentIntents,
@@ -1191,10 +1214,14 @@
      * </p>
      *
      * @see #sendMultipartTextMessage(String, String, ArrayList, ArrayList, ArrayList)
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      * @hide
      **/
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public void sendMultipartTextMessageWithoutPersisting(
             String destinationAddress, String scAddress, List<String> parts,
             List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents) {
@@ -1498,7 +1525,10 @@
      *  raw pdu of the status report is in the extended data ("pdu").
      *
      * @throws IllegalArgumentException if destinationAddress or data are empty
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public void sendDataMessage(
             String destinationAddress, String scAddress, short destinationPort,
             byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
@@ -1609,6 +1639,7 @@
      * .{@link #createForSubscriptionId createForSubscriptionId(subId)} instead
      */
     @Deprecated
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public static SmsManager getSmsManagerForSubscriptionId(int subId) {
         return getSmsManagerForContextAndSubscriptionId(null, subId);
     }
@@ -1626,6 +1657,7 @@
      * @see SubscriptionManager#getActiveSubscriptionInfoList()
      * @see SubscriptionManager#getDefaultSmsSubscriptionId()
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public @NonNull SmsManager createForSubscriptionId(int subId) {
         return getSmsManagerForContextAndSubscriptionId(mContext, subId);
     }
@@ -1651,7 +1683,11 @@
      * @return associated subscription ID or {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} if
      * the default subscription id cannot be determined or the device has multiple active
      * subscriptions and and no default is set ("ask every time") by the user.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public int getSubscriptionId() {
         try {
             return (mSubId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)
@@ -2018,10 +2054,14 @@
      *
      * @throws IllegalArgumentException if endMessageId < startMessageId
      * @deprecated Use {@link TelephonyManager#setCellBroadcastIdRanges} instead.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      * {@hide}
      */
     @Deprecated
     @SystemApi
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public boolean enableCellBroadcastRange(int startMessageId, int endMessageId,
             @android.telephony.SmsCbMessage.MessageFormat int ranType) {
         boolean success = false;
@@ -2079,11 +2119,15 @@
      * @see #enableCellBroadcastRange(int, int, int)
      *
      * @throws IllegalArgumentException if endMessageId < startMessageId
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
+     *
      * @deprecated Use {@link TelephonyManager#setCellBroadcastIdRanges} instead.
      * {@hide}
      */
     @Deprecated
     @SystemApi
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public boolean disableCellBroadcastRange(int startMessageId, int endMessageId,
             @android.telephony.SmsCbMessage.MessageFormat int ranType) {
         boolean success = false;
@@ -2223,7 +2267,11 @@
      * @return the user-defined default SMS subscription id, or the active subscription id if
      * there's only one active subscription available, otherwise
      * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public static int getDefaultSmsSubscriptionId() {
         try {
             return getISmsService().getPreferredSmsSubscription();
@@ -2271,10 +2319,14 @@
      * </p>
      *
      * @return the total number of SMS records which can be stored on the SIM card.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
     @RequiresPermission(anyOf = {android.Manifest.permission.READ_PHONE_STATE,
             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE})
     @IntRange(from = 0)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public int getSmsCapacityOnIcc() {
         int ret = 0;
         try {
@@ -2819,7 +2871,10 @@
      * <code>MMS_ERROR_DATA_DISABLED</code><br>
      * <code>MMS_ERROR_MMS_DISABLED_BY_CARRIER</code><br>
      * @throws IllegalArgumentException if contentUri is empty
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public void sendMultimediaMessage(Context context, Uri contentUri, String locationUrl,
             Bundle configOverrides, PendingIntent sentIntent) {
         sendMultimediaMessage(context, contentUri, locationUrl, configOverrides, sentIntent,
@@ -2863,7 +2918,10 @@
      * @param messageId an id that uniquely identifies the message requested to be sent.
      * Used for logging and diagnostics purposes. The id may be 0.
      * @throws IllegalArgumentException if contentUri is empty
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public void sendMultimediaMessage(@NonNull Context context, @NonNull Uri contentUri,
             @Nullable String locationUrl,
             @SuppressWarnings("NullableCollection") @Nullable Bundle configOverrides,
@@ -2922,7 +2980,10 @@
      * <code>MMS_ERROR_DATA_DISABLED</code><br>
      * <code>MMS_ERROR_MMS_DISABLED_BY_CARRIER</code><br>
      * @throws IllegalArgumentException if locationUrl or contentUri is empty
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public void downloadMultimediaMessage(Context context, String locationUrl, Uri contentUri,
             Bundle configOverrides, PendingIntent downloadedIntent) {
         downloadMultimediaMessage(context, locationUrl, contentUri, configOverrides,
@@ -2968,7 +3029,10 @@
      * @param messageId an id that uniquely identifies the message requested to be downloaded.
      * Used for logging and diagnostics purposes. The id may be 0.
      * @throws IllegalArgumentException if locationUrl or contentUri is empty
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public void downloadMultimediaMessage(@NonNull Context context, @NonNull String locationUrl,
             @NonNull Uri contentUri,
             @SuppressWarnings("NullableCollection") @Nullable Bundle configOverrides,
@@ -3079,7 +3143,11 @@
      *
      * @return the bundle key/values pairs that contains MMS configuration values
      *  or an empty Bundle if they cannot be found.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     @NonNull public Bundle getCarrierConfigValues() {
         try {
             ISms iSms = getISmsService();
@@ -3115,7 +3183,11 @@
      *
      * @return Token to include in an SMS message. The token will be 11 characters long.
      * @see android.provider.Telephony.Sms.Intents#getMessagesFromIntent
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public String createAppSpecificSmsToken(PendingIntent intent) {
         try {
             ISms iccSms = getISmsServiceOrThrow();
@@ -3233,7 +3305,11 @@
      *  message.
      * @param intent this intent is sent when the matching SMS message is received.
      * @return Token to include in an SMS message.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     @Nullable
     public String createAppSpecificSmsTokenWithPackageInfo(
             @Nullable String prefixes, @NonNull PendingIntent intent) {
@@ -3393,9 +3469,13 @@
      * </p>
      *
      * @return the SMSC address string, null if failed.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
     @SuppressAutoDoc // for carrier privileges and default SMS application.
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     @Nullable
     public String getSmscAddress() {
         String smsc = null;
@@ -3430,9 +3510,13 @@
      *
      * @param smsc the SMSC address string.
      * @return true for success, false otherwise. Failure can be due modem returning an error.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
     @SuppressAutoDoc // for carrier privileges and default SMS application.
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public boolean setSmscAddress(@NonNull String smsc) {
         try {
             ISms iSms = getISmsService();
@@ -3455,10 +3539,14 @@
      *  {@link SmsManager#PREMIUM_SMS_CONSENT_ASK_USER},
      *  {@link SmsManager#PREMIUM_SMS_CONSENT_NEVER_ALLOW}, or
      *  {@link SmsManager#PREMIUM_SMS_CONSENT_ALWAYS_ALLOW}
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public @PremiumSmsConsent int getPremiumSmsConsent(@NonNull String packageName) {
         int permission = 0;
         try {
@@ -3479,10 +3567,14 @@
      * @param permission one of {@link SmsManager#PREMIUM_SMS_CONSENT_ASK_USER},
      *  {@link SmsManager#PREMIUM_SMS_CONSENT_NEVER_ALLOW}, or
      *  {@link SmsManager#PREMIUM_SMS_CONSENT_ALWAYS_ALLOW}
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public void setPremiumSmsConsent(
             @NonNull String packageName, @PremiumSmsConsent int permission) {
         try {
@@ -3498,11 +3590,15 @@
     /**
      * Reset all cell broadcast ranges. Previously enabled ranges will become invalid after this.
      * @deprecated Use {@link TelephonyManager#setCellBroadcastIdRanges} with empty list instead
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      * @hide
      */
     @Deprecated
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public void resetAllCellBroadcastRanges() {
         try {
             ISms iSms = getISmsService();
@@ -3530,6 +3626,8 @@
      * available.
      * @throws SecurityException if the caller does not have the required permission/privileges.
      * @throws IllegalStateException in case of telephony service is not available.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @NonNull
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index ff7b392..5615602 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1757,6 +1757,9 @@
      *
      * @param subId The unique SubscriptionInfo key in database.
      * @return SubscriptionInfo, maybe null if its not active.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@@ -1790,6 +1793,8 @@
      * @param iccId the IccId of SIM card
      * @return SubscriptionInfo, maybe null if its not active
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -1826,6 +1831,9 @@
      *
      * @param slotIndex the slot which the subscription is inserted
      * @return SubscriptionInfo, maybe null if its not active
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@@ -1870,6 +1878,8 @@
      * {@link SubscriptionInfo#getSubscriptionId()}.
      *
      * @throws SecurityException if callers do not hold the required permission.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @NonNull
     @RequiresPermission(anyOf = {
@@ -1929,6 +1939,9 @@
      * then by {@link SubscriptionInfo#getSubscriptionId}.
      * </li>
      * </ul>
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     // @RequiresPermission(TODO(b/308809058))
@@ -1972,6 +1985,8 @@
      * This is similar to {@link #getActiveSubscriptionInfoList} except that it will return
      * both active and hidden SubscriptionInfos.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     public @NonNull List<SubscriptionInfo> getCompleteActiveSubscriptionInfoList() {
         List<SubscriptionInfo> completeList = getActiveSubscriptionInfoList(
@@ -2056,6 +2071,9 @@
      * <p>
      * Permissions android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE is required
      * for #getAvailableSubscriptionInfoList to be invoked.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -2097,6 +2115,9 @@
      * if the list is non-empty the list is sorted by {@link SubscriptionInfo#getSimSlotIndex}
      * then by {@link SubscriptionInfo#getSubscriptionId}.
      * </ul>
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
     public List<SubscriptionInfo> getAccessibleSubscriptionInfoList() {
         List<SubscriptionInfo> result = null;
@@ -2125,6 +2146,8 @@
      *
      * @see TelephonyManager#getCardIdForDefaultEuicc() for more information on the card ID.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      * @hide
      */
     @SystemApi
@@ -2155,6 +2178,8 @@
      *
      * @see TelephonyManager#getCardIdForDefaultEuicc() for more information on the card ID.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      * @hide
      */
     @SystemApi
@@ -2177,6 +2202,9 @@
      * @return The current number of active subscriptions.
      *
      * @see #getActiveSubscriptionInfoList()
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     // @RequiresPermission(TODO(b/308809058))
@@ -2247,6 +2275,9 @@
      * @param slotIndex the slot assigned to this subscription. It is ignored for subscriptionType
      *                  of {@link #SUBSCRIPTION_TYPE_REMOTE_SIM}.
      * @param subscriptionType the {@link #SUBSCRIPTION_TYPE}
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@@ -2289,6 +2320,8 @@
      * @throws NullPointerException if {@code uniqueId} is {@code null}.
      * @throws SecurityException if callers do not hold the required permission.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@@ -2435,6 +2468,7 @@
      * @deprecated Use {@link #getSubscriptionId(int)} instead.
      * @hide
      */
+    @Deprecated
     public static int[] getSubId(int slotIndex) {
         if (!isValidSlotIndex(slotIndex)) {
             return null;
@@ -2489,6 +2523,9 @@
      * On a data only device or on error, will return INVALID_SUBSCRIPTION_ID.
      *
      * @return the default voice subscription Id.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     public static int getDefaultVoiceSubscriptionId() {
         int subId = INVALID_SUBSCRIPTION_ID;
@@ -2516,6 +2553,9 @@
      *
      * @param subscriptionId A valid subscription ID to set as the system default, or
      *                       {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -2535,6 +2575,9 @@
     /**
      * Same as {@link #setDefaultVoiceSubscriptionId(int)}, but preserved for backwards
      * compatibility.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     public void setDefaultVoiceSubId(int subId) {
@@ -2578,6 +2621,8 @@
      *
      * @param subscriptionId the supplied subscription ID
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -2612,6 +2657,8 @@
      *
      * @param subscriptionId the supplied subscription ID
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -2634,6 +2681,9 @@
      * Will return null on voice only devices, or on error.
      *
      * @return the SubscriptionInfo for the default data subscription.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @UnsupportedAppUsage
@@ -2720,6 +2770,9 @@
      *
      * @return the list of subId's that are active,
      *         is never null but the length may be 0.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -2738,6 +2791,9 @@
      *
      * @return the list of subId's that are active,
      *         is never null but the length may be 0.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -2987,6 +3043,9 @@
      * @param context Context object
      * @param subId Subscription Id of Subscription whose resources are required
      * @return Resources associated with Subscription.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @NonNull
@@ -3069,6 +3128,9 @@
      * @return {@code true} if the supplied subscription ID corresponds to an active subscription;
      * {@code false} if it does not correspond to an active subscription; or throw a
      * SecurityException if the caller hasn't got the right permission.
+     *i
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public boolean isActiveSubscriptionId(int subscriptionId) {
@@ -3377,6 +3439,8 @@
      *
      * @throws IllegalStateException when subscription manager service is not available.
      * @throws SecurityException when clients do not have MODIFY_PHONE_STATE permission.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -3454,6 +3518,9 @@
      * {@link TelephonyManager#hasCarrierPrivileges}).
      *
      * @return the list of opportunistic subscription info. If none exists, an empty list.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@@ -3489,8 +3556,12 @@
      *  PendingIntent)} and does not support Multiple Enabled Profile(MEP). Apps should use
      *  {@link EuiccManager#switchToSubscription(int, PendingIntent)} or
      *  {@link EuiccManager#switchToSubscription(int, int, PendingIntent)} instead.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
     @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_EUICC)
     @Deprecated
     public void switchToSubscription(int subId, @NonNull PendingIntent callbackIntent) {
         Preconditions.checkNotNull(callbackIntent, "callbackIntent cannot be null");
@@ -3518,6 +3589,9 @@
      * @param opportunistic whether it’s opportunistic subscription.
      * @param subId the unique SubscriptionInfo index in database
      * @return {@code true} if the operation is succeed, {@code false} otherwise.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -3554,6 +3628,8 @@
      *             outlined above.
      * @throws IllegalArgumentException if any of the subscriptions in the list doesn't exist.
      * @throws IllegalStateException if Telephony service is in bad state.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      *
      * @param subIdList list of subId that will be in the same group
      * @return groupUUID a UUID assigned to the subscription group.
@@ -3598,6 +3674,8 @@
      *             outlined above.
      * @throws IllegalArgumentException if the some subscriptions in the list doesn't exist.
      * @throws IllegalStateException if Telephony service is in bad state.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      *
      * @param subIdList list of subId that need adding into the group
      * @param groupUuid the groupUuid the subscriptions are being added to.
@@ -3647,6 +3725,8 @@
      * @throws IllegalArgumentException if the some subscriptions in the list doesn't belong the
      * specified group.
      * @throws IllegalStateException if Telephony service is in bad state.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      *
      * @see #createSubscriptionGroup(List)
      */
@@ -3696,6 +3776,8 @@
      * @throws IllegalStateException if Telephony service is in bad state.
      * @throws SecurityException if the caller doesn't meet the requirements
      *             outlined above.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      *
      * @param groupUuid of which list of subInfo will be returned.
      * @return list of subscriptionInfo that belong to the same group, including the given
@@ -3817,6 +3899,8 @@
      *
      * @return whether the operation is successful.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -3844,6 +3928,9 @@
      *
      * @param subscriptionId which subscription to operate on.
      * @param enabled whether uicc applications are enabled or disabled.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -3872,6 +3959,8 @@
      *
      * @return whether can disable subscriptions on physical SIMs.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -3897,6 +3986,8 @@
      *
      * @param subscriptionId The subscription id.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -3923,6 +4014,8 @@
      * @param sharing The status sharing preference.
      *
      * @throws SecurityException if the caller doesn't have permissions required.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setDeviceToDeviceStatusSharingPreference(int subscriptionId,
@@ -3941,6 +4034,8 @@
      * @return The device to device status sharing preference
      *
      * @throws SecurityException if the caller doesn't have permissions required.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     public @DeviceToDeviceStatusSharingPreference int getDeviceToDeviceStatusSharingPreference(
             int subscriptionId) {
@@ -3960,6 +4055,8 @@
      * @param contacts The list of contacts that allow device to device status sharing.
      *
      * @throws SecurityException if the caller doesn't have permissions required.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setDeviceToDeviceStatusSharingContacts(int subscriptionId,
@@ -3980,6 +4077,9 @@
      * @param subscriptionId Subscription id.
      *
      * @return The list of contacts that allow device to device status sharing.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     public @NonNull List<Uri> getDeviceToDeviceStatusSharingContacts(int subscriptionId) {
         String result = getStringSubscriptionProperty(mContext, subscriptionId,
@@ -4012,6 +4112,8 @@
      *
      * @throws IllegalArgumentException if the provided slot index is invalid.
      * @throws SecurityException if callers do not hold the required permission.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      *
      * @hide
      */
@@ -4152,6 +4254,8 @@
      *
      * @param data with the sim specific configs to be backed up.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -4206,6 +4310,8 @@
      * @throws IllegalArgumentException if {@code source} is invalid.
      * @throws IllegalStateException if the telephony process is not currently available.
      * @throws SecurityException if the caller doesn't have permissions required.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      *
      * @see #PHONE_NUMBER_SOURCE_UICC
      * @see #PHONE_NUMBER_SOURCE_CARRIER
@@ -4266,6 +4372,8 @@
      *
      * @throws IllegalStateException if the telephony process is not currently available.
      * @throws SecurityException if the caller doesn't have permissions required.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      *
      * @see #getPhoneNumber(int, int)
      */
@@ -4309,6 +4417,8 @@
      * @throws IllegalStateException if the telephony process is not currently available.
      * @throws NullPointerException if {@code number} is {@code null}.
      * @throws SecurityException if the caller doesn't have permissions required.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresPermission("carrier privileges")
     public void setCarrierPhoneNumber(int subscriptionId, @NonNull String number) {
diff --git a/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt b/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt
new file mode 100644
index 0000000..e2b0c36
--- /dev/null
+++ b/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt
@@ -0,0 +1,215 @@
+/*
+ * Copyright 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 android.hardware.input
+
+import android.content.Context
+import android.content.ContextWrapper
+import android.os.Handler
+import android.os.HandlerExecutor
+import android.os.test.TestLooper
+import android.platform.test.annotations.Presubmit
+import android.platform.test.flag.junit.SetFlagsRule
+import android.view.KeyEvent
+import androidx.test.core.app.ApplicationProvider
+import com.android.server.testutils.any
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.`when`
+import org.mockito.junit.MockitoJUnitRunner
+import kotlin.test.assertEquals
+import kotlin.test.assertNotNull
+import kotlin.test.assertNull
+import kotlin.test.assertTrue
+import kotlin.test.fail
+
+/**
+ * Tests for [InputManager.StickyModifierStateListener].
+ *
+ * Build/Install/Run:
+ * atest InputTests:StickyModifierStateListenerTest
+ */
+@Presubmit
+@RunWith(MockitoJUnitRunner::class)
+class StickyModifierStateListenerTest {
+
+    @get:Rule
+    val rule = SetFlagsRule()
+
+    private val testLooper = TestLooper()
+    private val executor = HandlerExecutor(Handler(testLooper.looper))
+    private var registeredListener: IStickyModifierStateListener? = null
+    private lateinit var context: Context
+    private lateinit var inputManager: InputManager
+    private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
+
+    @Mock
+    private lateinit var iInputManagerMock: IInputManager
+
+    @Before
+    fun setUp() {
+        // Enable Sticky keys feature
+        rule.enableFlags(com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG)
+        rule.enableFlags(com.android.input.flags.Flags.FLAG_ENABLE_INPUT_FILTER_RUST_IMPL)
+
+        context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
+        inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManagerMock)
+        inputManager = InputManager(context)
+        `when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
+                .thenReturn(inputManager)
+
+        // Handle sticky modifier state listener registration.
+        doAnswer {
+            val listener = it.getArgument(0) as IStickyModifierStateListener
+            if (registeredListener != null &&
+                    registeredListener!!.asBinder() != listener.asBinder()) {
+                // There can only be one registered sticky modifier state listener per process.
+                fail("Trying to register a new listener when one already exists")
+            }
+            registeredListener = listener
+            null
+        }.`when`(iInputManagerMock).registerStickyModifierStateListener(any())
+
+        // Handle sticky modifier state listener being unregistered.
+        doAnswer {
+            val listener = it.getArgument(0) as IStickyModifierStateListener
+            if (registeredListener == null ||
+                    registeredListener!!.asBinder() != listener.asBinder()) {
+                fail("Trying to unregister a listener that is not registered")
+            }
+            registeredListener = null
+            null
+        }.`when`(iInputManagerMock).unregisterStickyModifierStateListener(any())
+    }
+
+    @After
+    fun tearDown() {
+        if (this::inputManagerGlobalSession.isInitialized) {
+            inputManagerGlobalSession.close()
+        }
+    }
+
+    private fun notifyStickyModifierStateChanged(modifierState: Int, lockedModifierState: Int) {
+        registeredListener!!.onStickyModifierStateChanged(modifierState, lockedModifierState)
+    }
+
+    @Test
+    fun testListenerIsNotifiedOnModifierStateChanged() {
+        var callbackCount = 0
+
+        // Add a sticky modifier state listener
+        inputManager.registerStickyModifierStateListener(executor) {
+            callbackCount++
+        }
+
+        // Notifying sticky modifier state change will notify the listener.
+        notifyStickyModifierStateChanged(0, 0)
+        testLooper.dispatchNext()
+        assertEquals(1, callbackCount)
+    }
+
+    @Test
+    fun testListenerHasCorrectModifierStateNotified() {
+        // Add a sticky modifier state listener
+        inputManager.registerStickyModifierStateListener(executor) {
+            state: StickyModifierState ->
+            assertTrue(state.isAltModifierOn)
+            assertTrue(state.isAltModifierLocked)
+            assertTrue(state.isShiftModifierOn)
+            assertTrue(!state.isShiftModifierLocked)
+            assertTrue(!state.isCtrlModifierOn)
+            assertTrue(!state.isCtrlModifierLocked)
+            assertTrue(!state.isMetaModifierOn)
+            assertTrue(!state.isMetaModifierLocked)
+            assertTrue(!state.isAltGrModifierOn)
+            assertTrue(!state.isAltGrModifierLocked)
+        }
+
+        // Notifying sticky modifier state change will notify the listener.
+        notifyStickyModifierStateChanged(
+                KeyEvent.META_ALT_ON or KeyEvent.META_ALT_LEFT_ON or
+                        KeyEvent.META_SHIFT_ON or KeyEvent.META_SHIFT_LEFT_ON,
+                KeyEvent.META_ALT_ON or KeyEvent.META_ALT_LEFT_ON
+        )
+        testLooper.dispatchNext()
+    }
+
+    @Test
+    fun testAddingListenersRegistersInternalCallbackListener() {
+        // Set up two callbacks.
+        val callback1 = InputManager.StickyModifierStateListener {}
+        val callback2 = InputManager.StickyModifierStateListener {}
+
+        assertNull(registeredListener)
+
+        // Adding the listener should register the callback with InputManagerService.
+        inputManager.registerStickyModifierStateListener(executor, callback1)
+        assertNotNull(registeredListener)
+
+        // Adding another listener should not register new internal listener.
+        val currListener = registeredListener
+        inputManager.registerStickyModifierStateListener(executor, callback2)
+        assertEquals(currListener, registeredListener)
+    }
+
+    @Test
+    fun testRemovingListenersUnregistersInternalCallbackListener() {
+        // Set up two callbacks.
+        val callback1 = InputManager.StickyModifierStateListener {}
+        val callback2 = InputManager.StickyModifierStateListener {}
+
+        inputManager.registerStickyModifierStateListener(executor, callback1)
+        inputManager.registerStickyModifierStateListener(executor, callback2)
+
+        // Only removing all listeners should remove the internal callback
+        inputManager.unregisterStickyModifierStateListener(callback1)
+        assertNotNull(registeredListener)
+        inputManager.unregisterStickyModifierStateListener(callback2)
+        assertNull(registeredListener)
+    }
+
+    @Test
+    fun testMultipleListeners() {
+        // Set up two callbacks.
+        var callbackCount1 = 0
+        var callbackCount2 = 0
+        val callback1 = InputManager.StickyModifierStateListener { _ -> callbackCount1++ }
+        val callback2 = InputManager.StickyModifierStateListener { _ -> callbackCount2++ }
+
+        // Add both sticky modifier state listeners
+        inputManager.registerStickyModifierStateListener(executor, callback1)
+        inputManager.registerStickyModifierStateListener(executor, callback2)
+
+        // Notifying sticky modifier state change trigger the both callbacks.
+        notifyStickyModifierStateChanged(0, 0)
+        testLooper.dispatchAll()
+        assertEquals(1, callbackCount1)
+        assertEquals(1, callbackCount2)
+
+        inputManager.unregisterStickyModifierStateListener(callback2)
+        // Notifying sticky modifier state change should still trigger callback1 but not callback2.
+        notifyStickyModifierStateChanged(0, 0)
+        testLooper.dispatchAll()
+        assertEquals(2, callbackCount1)
+        assertEquals(1, callbackCount2)
+    }
+}