Merge "Add support for error dialogs and confirmation during unarchival" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index c477a75..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",
@@ -256,7 +262,6 @@
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
 
-
 // OS
 aconfig_declarations {
     name: "android.os.flags-aconfig",
diff --git a/Android.bp b/Android.bp
index f6a9328..b139b7e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -132,6 +132,7 @@
         ":libcamera_client_aidl",
         ":libcamera_client_framework_aidl",
         ":libupdate_engine_aidl",
+        ":libupdate_engine_stable-V2-java-source",
         ":logd_aidl",
         ":resourcemanager_aidl",
         ":storaged_aidl",
diff --git a/core/api/current.txt b/core/api/current.txt
index 8109e04..c7b921c 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -22102,7 +22102,7 @@
   }
 
   @FlaggedApi("android.media.audio.loudness_configurator_api") public class LoudnessCodecConfigurator {
-    method @FlaggedApi("android.media.audio.loudness_configurator_api") public void addMediaCodec(@NonNull android.media.MediaCodec);
+    method @FlaggedApi("android.media.audio.loudness_configurator_api") public boolean addMediaCodec(@NonNull android.media.MediaCodec);
     method @FlaggedApi("android.media.audio.loudness_configurator_api") @NonNull public static android.media.LoudnessCodecConfigurator create();
     method @FlaggedApi("android.media.audio.loudness_configurator_api") @NonNull public static android.media.LoudnessCodecConfigurator create(@NonNull java.util.concurrent.Executor, @NonNull android.media.LoudnessCodecConfigurator.OnLoudnessCodecUpdateListener);
     method @FlaggedApi("android.media.audio.loudness_configurator_api") @NonNull public android.os.Bundle getLoudnessCodecParams(@NonNull android.media.AudioTrack, @NonNull android.media.MediaCodec);
@@ -28825,6 +28825,8 @@
     method public boolean isSecureNfcEnabled();
     method public boolean isSecureNfcSupported();
     method @FlaggedApi("android.nfc.enable_nfc_charging") public boolean isWlcEnabled();
+    method @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public void resetDiscoveryTechnology(@NonNull android.app.Activity);
+    method @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public void setDiscoveryTechnology(@NonNull android.app.Activity, int, int);
     field public static final String ACTION_ADAPTER_STATE_CHANGED = "android.nfc.action.ADAPTER_STATE_CHANGED";
     field public static final String ACTION_NDEF_DISCOVERED = "android.nfc.action.NDEF_DISCOVERED";
     field @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public static final String ACTION_PREFERRED_PAYMENT_CHANGED = "android.nfc.action.PREFERRED_PAYMENT_CHANGED";
@@ -28840,6 +28842,13 @@
     field public static final String EXTRA_READER_PRESENCE_CHECK_DELAY = "presence";
     field public static final String EXTRA_SECURE_ELEMENT_NAME = "android.nfc.extra.SECURE_ELEMENT_NAME";
     field public static final String EXTRA_TAG = "android.nfc.extra.TAG";
+    field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_LISTEN_DISABLE = 0; // 0x0
+    field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_LISTEN_KEEP = -1; // 0xffffffff
+    field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_LISTEN_NFC_PASSIVE_A = 1; // 0x1
+    field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_LISTEN_NFC_PASSIVE_B = 2; // 0x2
+    field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_LISTEN_NFC_PASSIVE_F = 4; // 0x4
+    field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_READER_DISABLE = 0; // 0x0
+    field @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public static final int FLAG_READER_KEEP = -1; // 0xffffffff
     field public static final int FLAG_READER_NFC_A = 1; // 0x1
     field public static final int FLAG_READER_NFC_B = 2; // 0x2
     field public static final int FLAG_READER_NFC_BARCODE = 16; // 0x10
@@ -53958,8 +53967,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/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 014ddd41..705b2ee 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -3481,24 +3481,26 @@
         }
         mResources = r;
 
-        // only do this if the user already has more than one preferred locale
-        if (android.content.res.Flags.defaultLocale()
-                && r.getConfiguration().getLocales().size() > 1) {
-            LocaleConfig lc;
-            if (getUserId() < 0) {
-                lc = LocaleConfig.fromContextIgnoringOverride(this);
-            } else {
-                // This is needed because the app might have locale config overrides that need to
-                // be read from disk in order for resources to correctly choose which values to
-                // load.
-                StrictMode.ThreadPolicy policy = StrictMode.allowThreadDiskReads();
-                try {
-                    lc = new LocaleConfig(this);
-                } finally {
-                    StrictMode.setThreadPolicy(policy);
+        if (r != null) {
+            // only do this if the user already has more than one preferred locale
+            if (android.content.res.Flags.defaultLocale()
+                    && r.getConfiguration().getLocales().size() > 1) {
+                LocaleConfig lc;
+                if (getUserId() < 0) {
+                    lc = LocaleConfig.fromContextIgnoringOverride(this);
+                } else {
+                    // This is needed because the app might have locale config overrides that need
+                    // to be read from disk in order for resources to correctly choose which values
+                    // to load.
+                    StrictMode.ThreadPolicy policy = StrictMode.allowThreadDiskReads();
+                    try {
+                        lc = new LocaleConfig(this);
+                    } finally {
+                        StrictMode.setThreadPolicy(policy);
+                    }
                 }
+                mResourcesManager.setLocaleConfig(lc);
             }
-            mResourcesManager.setLocaleConfig(lc);
         }
     }
 
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/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/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index 967a0cc..286cf28 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -95,4 +95,6 @@
     void registerWlcStateListener(in INfcWlcStateListener listener);
     void unregisterWlcStateListener(in INfcWlcStateListener listener);
     WlcLDeviceInfo getWlcLDeviceInfo();
+
+    void updateDiscoveryTechnology(IBinder b, int pollFlags, int listenFlags);
 }
diff --git a/core/java/android/nfc/INfcCardEmulation.aidl b/core/java/android/nfc/INfcCardEmulation.aidl
index 191385a..f4b4604 100644
--- a/core/java/android/nfc/INfcCardEmulation.aidl
+++ b/core/java/android/nfc/INfcCardEmulation.aidl
@@ -43,4 +43,7 @@
     ApduServiceInfo getPreferredPaymentService(int userHandle);
     boolean setServiceEnabledForCategoryOther(int userHandle, in ComponentName app, boolean status);
     boolean isDefaultPaymentRegistered();
+
+    boolean overrideRoutingTable(int userHandle, String protocol, String technology);
+    boolean recoverRoutingTable(int userHandle);
 }
diff --git a/core/java/android/nfc/NfcActivityManager.java b/core/java/android/nfc/NfcActivityManager.java
index 8d75cac..f03fc0a 100644
--- a/core/java/android/nfc/NfcActivityManager.java
+++ b/core/java/android/nfc/NfcActivityManager.java
@@ -112,6 +112,9 @@
         Bundle readerModeExtras = null;
         Binder token;
 
+        int mPollTech = NfcAdapter.FLAG_USE_ALL_TECH;
+        int mListenTech = NfcAdapter.FLAG_USE_ALL_TECH;
+
         public NfcActivityState(Activity activity) {
             if (activity.isDestroyed()) {
                 throw new IllegalStateException("activity is already destroyed");
@@ -132,6 +135,9 @@
             readerModeFlags = 0;
             readerModeExtras = null;
             token = null;
+
+            mPollTech = NfcAdapter.FLAG_USE_ALL_TECH;
+            mListenTech = NfcAdapter.FLAG_USE_ALL_TECH;
         }
         @Override
         public String toString() {
@@ -278,6 +284,9 @@
         int readerModeFlags = 0;
         Bundle readerModeExtras = null;
         Binder token;
+        int pollTech;
+        int listenTech;
+
         synchronized (NfcActivityManager.this) {
             NfcActivityState state = findActivityState(activity);
             if (DBG) Log.d(TAG, "onResume() for " + activity + " " + state);
@@ -286,9 +295,15 @@
             token = state.token;
             readerModeFlags = state.readerModeFlags;
             readerModeExtras = state.readerModeExtras;
+
+            pollTech = state.mPollTech;
+            listenTech = state.mListenTech;
         }
         if (readerModeFlags != 0) {
             setReaderMode(token, readerModeFlags, readerModeExtras);
+        } else if (listenTech != NfcAdapter.FLAG_USE_ALL_TECH
+                || pollTech != NfcAdapter.FLAG_USE_ALL_TECH) {
+            changeDiscoveryTech(token, pollTech, listenTech);
         }
         requestNfcServiceCallback();
     }
@@ -298,6 +313,9 @@
     public void onActivityPaused(Activity activity) {
         boolean readerModeFlagsSet;
         Binder token;
+        int pollTech;
+        int listenTech;
+
         synchronized (NfcActivityManager.this) {
             NfcActivityState state = findActivityState(activity);
             if (DBG) Log.d(TAG, "onPause() for " + activity + " " + state);
@@ -305,10 +323,17 @@
             state.resumed = false;
             token = state.token;
             readerModeFlagsSet = state.readerModeFlags != 0;
+
+            pollTech = state.mPollTech;
+            listenTech = state.mListenTech;
         }
         if (readerModeFlagsSet) {
             // Restore default p2p modes
             setReaderMode(token, 0, null);
+        } else if (listenTech != NfcAdapter.FLAG_USE_ALL_TECH
+                || pollTech != NfcAdapter.FLAG_USE_ALL_TECH) {
+            changeDiscoveryTech(token,
+                    NfcAdapter.FLAG_USE_ALL_TECH, NfcAdapter.FLAG_USE_ALL_TECH);
         }
     }
 
@@ -333,4 +358,53 @@
         }
     }
 
+    /** setDiscoveryTechnology() implementation */
+    public void setDiscoveryTech(Activity activity, int pollTech, int listenTech) {
+        boolean isResumed;
+        Binder token;
+        boolean readerModeFlagsSet;
+        synchronized (NfcActivityManager.this) {
+            NfcActivityState state = getActivityState(activity);
+            readerModeFlagsSet = state.readerModeFlags != 0;
+            state.mListenTech = listenTech;
+            state.mPollTech = pollTech;
+            token = state.token;
+            isResumed = state.resumed;
+        }
+        if (!readerModeFlagsSet && isResumed) {
+            changeDiscoveryTech(token, pollTech, listenTech);
+        } else if (readerModeFlagsSet) {
+            throw new IllegalStateException("Cannot be used when the Reader Mode is enabled");
+        }
+    }
+
+    /** resetDiscoveryTechnology() implementation */
+    public void resetDiscoveryTech(Activity activity) {
+        boolean isResumed;
+        Binder token;
+        boolean readerModeFlagsSet;
+        synchronized (NfcActivityManager.this) {
+            NfcActivityState state = getActivityState(activity);
+            readerModeFlagsSet = state.readerModeFlags != 0;
+            state.mListenTech = NfcAdapter.FLAG_USE_ALL_TECH;
+            state.mPollTech = NfcAdapter.FLAG_USE_ALL_TECH;
+            token = state.token;
+            isResumed = state.resumed;
+        }
+        if (readerModeFlagsSet) {
+            disableReaderMode(activity);
+        } else if (isResumed) {
+            changeDiscoveryTech(token, NfcAdapter.FLAG_USE_ALL_TECH, NfcAdapter.FLAG_USE_ALL_TECH);
+        }
+
+    }
+
+    private void changeDiscoveryTech(Binder token, int pollTech, int listenTech) {
+        try {
+            NfcAdapter.sService.updateDiscoveryTechnology(token, pollTech, listenTech);
+        } catch (RemoteException e) {
+            mAdapter.attemptDeadServiceRecovery(e);
+        }
+    }
+
 }
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 21e23ae..5a40e42 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -333,6 +333,19 @@
      */
     public static final int FLAG_READER_NFC_BARCODE = 0x10;
 
+    /** @hide */
+    @IntDef(flag = true, prefix = {"FLAG_READER_"}, value = {
+        FLAG_READER_KEEP,
+        FLAG_READER_DISABLE,
+        FLAG_READER_NFC_A,
+        FLAG_READER_NFC_B,
+        FLAG_READER_NFC_F,
+        FLAG_READER_NFC_V,
+        FLAG_READER_NFC_BARCODE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PollTechnology {}
+
     /**
      * Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}.
      * <p>
@@ -360,6 +373,76 @@
     public static final String EXTRA_READER_PRESENCE_CHECK_DELAY = "presence";
 
     /**
+     * Flag for use with {@link #setDiscoveryTechnology(Activity, int, int)}.
+     * <p>
+     * Setting this flag enables listening for Nfc-A technology.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
+    public static final int FLAG_LISTEN_NFC_PASSIVE_A = 0x1;
+
+    /**
+     * Flag for use with {@link #setDiscoveryTechnology(Activity, int, int)}.
+     * <p>
+     * Setting this flag enables listening for Nfc-B technology.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
+    public static final int FLAG_LISTEN_NFC_PASSIVE_B = 1 << 1;
+
+    /**
+     * Flag for use with {@link #setDiscoveryTechnology(Activity, int, int)}.
+     * <p>
+     * Setting this flag enables listening for Nfc-F technology.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
+    public static final int FLAG_LISTEN_NFC_PASSIVE_F = 1 << 2;
+
+    /**
+     * Flags for use with {@link #setDiscoveryTechnology(Activity, int, int)}.
+     * <p>
+     * Setting this flag disables listening.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
+    public static final int FLAG_LISTEN_DISABLE = 0x0;
+
+    /**
+     * Flags for use with {@link #setDiscoveryTechnology(Activity, int, int)}.
+     * <p>
+     * Setting this flag disables polling.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
+    public static final int FLAG_READER_DISABLE = 0x0;
+
+    /**
+     * Flags for use with {@link #setDiscoveryTechnology(Activity, int, int)}.
+     * <p>
+     * Setting this flag makes listening to use current flags.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
+    public static final int FLAG_LISTEN_KEEP = -1;
+
+    /**
+     * Flags for use with {@link #setDiscoveryTechnology(Activity, int, int)}.
+     * <p>
+     * Setting this flag makes polling to use current flags.
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
+    public static final int FLAG_READER_KEEP = -1;
+
+    /** @hide */
+    public static final int FLAG_USE_ALL_TECH = 0xff;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = {"FLAG_LISTEN_"}, value = {
+        FLAG_LISTEN_KEEP,
+        FLAG_LISTEN_DISABLE,
+        FLAG_LISTEN_NFC_PASSIVE_A,
+        FLAG_LISTEN_NFC_PASSIVE_B,
+        FLAG_LISTEN_NFC_PASSIVE_F
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ListenTechnology {}
+
+    /**
      * @hide
      * @removed
      */
@@ -437,12 +520,14 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface TagIntentAppPreferenceResult {}
 
-    // Guarded by NfcAdapter.class
+    // Guarded by sLock
     static boolean sIsInitialized = false;
     static boolean sHasNfcFeature;
     static boolean sHasCeFeature;
     static boolean sHasNfcWlcFeature;
 
+    static Object sLock = new Object();
+
     // Final after first constructor, except for
     // attemptDeadServiceRecovery() when NFC crashes - we accept a best effort
     // recovery
@@ -1235,7 +1320,7 @@
     @java.lang.Deprecated
     @UnsupportedAppUsage
     public void setBeamPushUris(Uri[] uris, Activity activity) {
-        synchronized (NfcAdapter.class) {
+        synchronized (sLock) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
@@ -1305,7 +1390,7 @@
     @java.lang.Deprecated
     @UnsupportedAppUsage
     public void setBeamPushUrisCallback(CreateBeamUrisCallback callback, Activity activity) {
-        synchronized (NfcAdapter.class) {
+        synchronized (sLock) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
@@ -1390,7 +1475,7 @@
     @UnsupportedAppUsage
     public void setNdefPushMessage(NdefMessage message, Activity activity,
             Activity ... activities) {
-        synchronized (NfcAdapter.class) {
+        synchronized (sLock) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
@@ -1404,7 +1489,7 @@
     @SystemApi
     @UnsupportedAppUsage
     public void setNdefPushMessage(NdefMessage message, Activity activity, int flags) {
-        synchronized (NfcAdapter.class) {
+        synchronized (sLock) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
@@ -1483,7 +1568,7 @@
     @UnsupportedAppUsage
     public void setNdefPushMessageCallback(CreateNdefMessageCallback callback, Activity activity,
             Activity ... activities) {
-        synchronized (NfcAdapter.class) {
+        synchronized (sLock) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
@@ -1534,7 +1619,7 @@
     @UnsupportedAppUsage
     public void setOnNdefPushCompleteCallback(OnNdefPushCompleteCallback callback,
             Activity activity, Activity ... activities) {
-        synchronized (NfcAdapter.class) {
+        synchronized (sLock) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
@@ -1577,7 +1662,7 @@
      */
     public void enableForegroundDispatch(Activity activity, PendingIntent intent,
             IntentFilter[] filters, String[][] techLists) {
-        synchronized (NfcAdapter.class) {
+        synchronized (sLock) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
@@ -1612,7 +1697,7 @@
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
      */
     public void disableForegroundDispatch(Activity activity) {
-        synchronized (NfcAdapter.class) {
+        synchronized (sLock) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
@@ -1648,7 +1733,7 @@
      */
     public void enableReaderMode(Activity activity, ReaderCallback callback, int flags,
             Bundle extras) {
-        synchronized (NfcAdapter.class) {
+        synchronized (sLock) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
@@ -1665,7 +1750,7 @@
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
      */
     public void disableReaderMode(Activity activity) {
-        synchronized (NfcAdapter.class) {
+        synchronized (sLock) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
@@ -1693,7 +1778,7 @@
     @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
     @SuppressLint("VisiblySynchronized")
     public void setReaderMode(boolean enablePolling) {
-        synchronized (NfcAdapter.class) {
+        synchronized (sLock) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
@@ -1708,6 +1793,80 @@
     }
 
     /**
+     * Set the NFC controller to enable specific poll/listen technologies,
+     * as specified in parameters, while this Activity is in the foreground.
+     *
+     * Use {@link #FLAG_READER_KEEP} to keep current polling technology.
+     * Use {@link #FLAG_LISTEN_KEEP} to keep current listenig technology.
+     * Use {@link #FLAG_READER_DISABLE} to disable polling.
+     * Use {@link #FLAG_LISTEN_DISABLE} to disable listening.
+     * Also refer to {@link #resetDiscoveryTechnology(Activity)} to restore these changes.
+     * </p>
+     * The pollTech, listenTech parameters can be one or several of below list.
+     * <pre>
+     *                    Poll                    Listen
+     *  Passive A         0x01   (NFC_A)           0x01  (NFC_PASSIVE_A)
+     *  Passive B         0x02   (NFC_B)           0x02  (NFC_PASSIVE_B)
+     *  Passive F         0x04   (NFC_F)           0x04  (NFC_PASSIVE_F)
+     *  ISO 15693         0x08   (NFC_V)             -
+     *  Kovio             0x10   (NFC_BARCODE)       -
+     * </pre>
+     * <p>Example usage in an Activity that requires to disable poll,
+     * keep current listen technologies:
+     * <pre>
+     * protected void onResume() {
+     *     mNfcAdapter = NfcAdapter.getDefaultAdapter(getApplicationContext());
+     *     mNfcAdapter.setDiscoveryTechnology(this,
+     *         NfcAdapter.FLAG_READER_DISABLE, NfcAdapter.FLAG_LISTEN_KEEP);
+     * }</pre></p>
+     * @param activity The Activity that requests NFC controller to enable specific technologies.
+     * @param pollTech Flags indicating poll technologies.
+     * @param listenTech Flags indicating listen technologies.
+     * @throws UnsupportedOperationException if FEATURE_NFC,
+     * FEATURE_NFC_HOST_CARD_EMULATION, FEATURE_NFC_HOST_CARD_EMULATION_NFCF are unavailable.
+     */
+
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
+    public void setDiscoveryTechnology(@NonNull Activity activity,
+            @PollTechnology int pollTech, @ListenTechnology int listenTech) {
+        if (listenTech == FLAG_LISTEN_DISABLE) {
+            synchronized (sLock) {
+                if (!sHasNfcFeature) {
+                    throw new UnsupportedOperationException();
+                }
+            }
+            mNfcActivityManager.enableReaderMode(activity, null, pollTech, null);
+            return;
+        }
+        if (pollTech == FLAG_READER_DISABLE) {
+            synchronized (sLock) {
+                if (!sHasCeFeature) {
+                    throw new UnsupportedOperationException();
+                }
+            }
+        } else {
+            synchronized (sLock) {
+                if (!sHasNfcFeature || !sHasCeFeature) {
+                    throw new UnsupportedOperationException();
+                }
+            }
+        }
+        mNfcActivityManager.setDiscoveryTech(activity, pollTech, listenTech);
+    }
+
+    /**
+     * Restore the poll/listen technologies of NFC controller,
+     * which were changed by {@link #setDiscoveryTechnology(Activity , int , int)}
+     *
+     * @param activity The Activity that requests to changed technologies.
+     */
+
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_SET_DISCOVERY_TECH)
+    public void resetDiscoveryTechnology(@NonNull Activity activity) {
+        mNfcActivityManager.resetDiscoveryTech(activity);
+    }
+
+    /**
      * Manually invoke Android Beam to share data.
      *
      * <p>The Android Beam animation is normally only shown when two NFC-capable
@@ -1737,7 +1896,7 @@
     @java.lang.Deprecated
     @UnsupportedAppUsage
     public boolean invokeBeam(Activity activity) {
-        synchronized (NfcAdapter.class) {
+        synchronized (sLock) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
@@ -1775,7 +1934,7 @@
     @Deprecated
     @UnsupportedAppUsage
     public void enableForegroundNdefPush(Activity activity, NdefMessage message) {
-        synchronized (NfcAdapter.class) {
+        synchronized (sLock) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
@@ -1805,7 +1964,7 @@
     @Deprecated
     @UnsupportedAppUsage
     public void disableForegroundNdefPush(Activity activity) {
-        synchronized (NfcAdapter.class) {
+        synchronized (sLock) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
@@ -2085,7 +2244,7 @@
     @java.lang.Deprecated
     @UnsupportedAppUsage
     public boolean isNdefPushEnabled() {
-        synchronized (NfcAdapter.class) {
+        synchronized (sLock) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
@@ -2199,7 +2358,7 @@
     @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
     public boolean addNfcUnlockHandler(final NfcUnlockHandler unlockHandler,
                                        String[] tagTechnologies) {
-        synchronized (NfcAdapter.class) {
+        synchronized (sLock) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
@@ -2248,7 +2407,7 @@
     @SystemApi
     @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
     public boolean removeNfcUnlockHandler(NfcUnlockHandler unlockHandler) {
-        synchronized (NfcAdapter.class) {
+        synchronized (sLock) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java
index 58b6179..ad86d70 100644
--- a/core/java/android/nfc/cardemulation/CardEmulation.java
+++ b/core/java/android/nfc/cardemulation/CardEmulation.java
@@ -998,6 +998,87 @@
         }
     }
 
+     /**
+      * Setting NFC controller routing table, which includes Protocol Route and Technology Route,
+      * while this Activity is in the foreground.
+      *
+      * The parameter set to null can be used to keep current values for that entry.
+      * <p>
+      * Example usage in an Activity that requires to set proto route to "ESE" and keep tech route:
+      * <pre>
+      * protected void onResume() {
+      *     mNfcAdapter.overrideRoutingTable(this , "ESE" , null);
+      * }</pre>
+      * </p>
+      * Also activities must call this method when it goes to the background,
+      * with all parameters set to null.
+      * @param activity The Activity that requests NFC controller routing table to be changed.
+      * @param protocol ISO-DEP route destination, which can be "DH" or "UICC" or "ESE".
+      * @param technology Tech-A, Tech-B route destination, which can be "DH" or "UICC" or "ESE".
+      * @return true if operation is successful and false otherwise
+      *
+      * This is a high risk API and only included to support mainline effort
+      * @hide
+      */
+    public boolean overrideRoutingTable(Activity activity, String protocol, String technology) {
+        if (activity == null) {
+            throw new NullPointerException("activity or service or category is null");
+        }
+        if (!activity.isResumed()) {
+            throw new IllegalArgumentException("Activity must be resumed.");
+        }
+        try {
+            return sService.overrideRoutingTable(UserHandle.myUserId(), protocol, technology);
+        } catch (RemoteException e) {
+            // Try one more time
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return false;
+            }
+            try {
+                return sService.overrideRoutingTable(UserHandle.myUserId(), protocol, technology);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach CardEmulationService.");
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Restore the NFC controller routing table,
+     * which was changed by {@link #overrideRoutingTable(Activity, String, String)}
+     *
+     * @param activity The Activity that requested NFC controller routing table to be changed.
+     * @return true if operation is successful and false otherwise
+     *
+     * @hide
+     */
+    public boolean recoverRoutingTable(Activity activity) {
+        if (activity == null) {
+            throw new NullPointerException("activity is null");
+        }
+        if (!activity.isResumed()) {
+            throw new IllegalArgumentException("Activity must be resumed.");
+        }
+        try {
+            return sService.recoverRoutingTable(UserHandle.myUserId());
+        } catch (RemoteException e) {
+            // Try one more time
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover CardEmulationService.");
+                return false;
+            }
+            try {
+                return sService.recoverRoutingTable(UserHandle.myUserId());
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach CardEmulationService.");
+                return false;
+            }
+        }
+    }
+
     void recoverService() {
         NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
         sService = adapter.getCardEmulationService();
diff --git a/core/java/android/nfc/flags.aconfig b/core/java/android/nfc/flags.aconfig
index ce4f777..01a4570 100644
--- a/core/java/android/nfc/flags.aconfig
+++ b/core/java/android/nfc/flags.aconfig
@@ -62,3 +62,10 @@
     description: "Flag for NFC charging changes"
     bug: "292143899"
 }
+
+flag {
+    name: "enable_nfc_set_discovery_tech"
+    namespace: "nfc"
+    description: "Flag for NFC set discovery tech API"
+    bug: "300351519"
+}
diff --git a/core/java/android/os/UpdateEngineStable.java b/core/java/android/os/UpdateEngineStable.java
new file mode 100644
index 0000000..9e2593e
--- /dev/null
+++ b/core/java/android/os/UpdateEngineStable.java
@@ -0,0 +1,192 @@
+/*
+ * 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 android.os;
+
+import android.annotation.IntDef;
+
+/**
+ * UpdateEngineStable handles calls to the update engine stalbe which takes care of A/B OTA updates.
+ * This interface has lesser functionalities than UpdateEngine and doesn't allow cancel.
+ *
+ * <p>The minimal flow is:
+ *
+ * <ol>
+ *   <li>Create a new UpdateEngineStable instance.
+ *   <li>Call {@link #bind}, provide callback function.
+ *   <li>Call {@link #applyPayloadFd}.
+ * </ol>
+ *
+ * The APIs defined in this class and UpdateEngineStableCallback class must be in sync with the ones
+ * in {@code system/update_engine/stable/android/os/IUpdateEngineStable.aidl} and {@code
+ * ssystem/update_engine/stable/android/os/IUpdateEngineStableCallback.aidl}.
+ *
+ * @hide
+ */
+public class UpdateEngineStable {
+    private static final String TAG = "UpdateEngineStable";
+
+    private static final String UPDATE_ENGINE_STABLE_SERVICE =
+            "android.os.UpdateEngineStableService";
+
+    /**
+     * Error codes from update engine upon finishing a call to {@link applyPayloadFd}. Values will
+     * be passed via the callback function {@link
+     * UpdateEngineStableCallback#onPayloadApplicationComplete}. Values must agree with the ones in
+     * {@code system/update_engine/common/error_code.h}.
+     */
+    /** @hide */
+    @IntDef(
+            value = {
+                UpdateEngine.ErrorCodeConstants.SUCCESS,
+                UpdateEngine.ErrorCodeConstants.ERROR,
+                UpdateEngine.ErrorCodeConstants.FILESYSTEM_COPIER_ERROR,
+                UpdateEngine.ErrorCodeConstants.POST_INSTALL_RUNNER_ERROR,
+                UpdateEngine.ErrorCodeConstants.PAYLOAD_MISMATCHED_TYPE_ERROR,
+                UpdateEngine.ErrorCodeConstants.INSTALL_DEVICE_OPEN_ERROR,
+                UpdateEngine.ErrorCodeConstants.KERNEL_DEVICE_OPEN_ERROR,
+                UpdateEngine.ErrorCodeConstants.DOWNLOAD_TRANSFER_ERROR,
+                UpdateEngine.ErrorCodeConstants.PAYLOAD_HASH_MISMATCH_ERROR,
+                UpdateEngine.ErrorCodeConstants.PAYLOAD_SIZE_MISMATCH_ERROR,
+                UpdateEngine.ErrorCodeConstants.DOWNLOAD_PAYLOAD_VERIFICATION_ERROR,
+                UpdateEngine.ErrorCodeConstants.PAYLOAD_TIMESTAMP_ERROR,
+                UpdateEngine.ErrorCodeConstants.UPDATED_BUT_NOT_ACTIVE,
+                UpdateEngine.ErrorCodeConstants.NOT_ENOUGH_SPACE,
+                UpdateEngine.ErrorCodeConstants.DEVICE_CORRUPTED,
+            })
+    public @interface ErrorCode {}
+
+    private final IUpdateEngineStable mUpdateEngineStable;
+    private IUpdateEngineStableCallback mUpdateEngineStableCallback = null;
+    private final Object mUpdateEngineStableCallbackLock = new Object();
+
+    /**
+     * Creates a new instance.
+     *
+     * @hide
+     */
+    public UpdateEngineStable() {
+        mUpdateEngineStable =
+                IUpdateEngineStable.Stub.asInterface(
+                        ServiceManager.getService(UPDATE_ENGINE_STABLE_SERVICE));
+        if (mUpdateEngineStable == null) {
+            throw new IllegalStateException("Failed to find " + UPDATE_ENGINE_STABLE_SERVICE);
+        }
+    }
+
+    /**
+     * Prepares this instance for use. The callback will be notified on any status change, and when
+     * the update completes. A handler can be supplied to control which thread runs the callback, or
+     * null.
+     *
+     * @hide
+     */
+    public boolean bind(final UpdateEngineStableCallback callback, final Handler handler) {
+        synchronized (mUpdateEngineStableCallbackLock) {
+            mUpdateEngineStableCallback =
+                    new IUpdateEngineStableCallback.Stub() {
+                        @Override
+                        public void onStatusUpdate(final int status, final float percent) {
+                            if (handler != null) {
+                                handler.post(
+                                        new Runnable() {
+                                            @Override
+                                            public void run() {
+                                                callback.onStatusUpdate(status, percent);
+                                            }
+                                        });
+                            } else {
+                                callback.onStatusUpdate(status, percent);
+                            }
+                        }
+
+                        @Override
+                        public void onPayloadApplicationComplete(final int errorCode) {
+                            if (handler != null) {
+                                handler.post(
+                                        new Runnable() {
+                                            @Override
+                                            public void run() {
+                                                callback.onPayloadApplicationComplete(errorCode);
+                                            }
+                                        });
+                            } else {
+                                callback.onPayloadApplicationComplete(errorCode);
+                            }
+                        }
+
+                        @Override
+                        public int getInterfaceVersion() {
+                            return super.VERSION;
+                        }
+
+                        @Override
+                        public String getInterfaceHash() {
+                            return super.HASH;
+                        }
+                    };
+
+            try {
+                return mUpdateEngineStable.bind(mUpdateEngineStableCallback);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Equivalent to {@code bind(callback, null)}.
+     *
+     * @hide
+     */
+    public boolean bind(final UpdateEngineStableCallback callback) {
+        return bind(callback, null);
+    }
+
+    /**
+     * Applies payload from given ParcelFileDescriptor. Usage is same as UpdateEngine#applyPayload
+     *
+     * @hide
+     */
+    public void applyPayloadFd(
+            ParcelFileDescriptor fd, long offset, long size, String[] headerKeyValuePairs) {
+        try {
+            mUpdateEngineStable.applyPayloadFd(fd, offset, size, headerKeyValuePairs);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Unbinds the last bound callback function.
+     *
+     * @hide
+     */
+    public boolean unbind() {
+        synchronized (mUpdateEngineStableCallbackLock) {
+            if (mUpdateEngineStableCallback == null) {
+                return true;
+            }
+            try {
+                boolean result = mUpdateEngineStable.unbind(mUpdateEngineStableCallback);
+                mUpdateEngineStableCallback = null;
+                return result;
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+}
diff --git a/core/java/android/os/UpdateEngineStableCallback.java b/core/java/android/os/UpdateEngineStableCallback.java
new file mode 100644
index 0000000..4bcfb4b
--- /dev/null
+++ b/core/java/android/os/UpdateEngineStableCallback.java
@@ -0,0 +1,46 @@
+/*
+ * 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 android.os;
+
+/**
+ * Callback function for UpdateEngineStable. Used to keep the caller up to date with progress, so
+ * the UI (if any) can be updated.
+ *
+ * <p>The APIs defined in this class and UpdateEngineStable class must be in sync with the ones in
+ * system/update_engine/stable/android/os/IUpdateEngineStable.aidl and
+ * system/update_engine/stable/android/os/IUpdateEngineStableCallback.aidl.
+ *
+ * <p>{@hide}
+ */
+public abstract class UpdateEngineStableCallback {
+
+    /**
+     * Invoked when anything changes. The value of {@code status} will be one of the values from
+     * {@link UpdateEngine.UpdateStatusConstants}, and {@code percent} will be valid
+     *
+     * @hide
+     */
+    public abstract void onStatusUpdate(int status, float percent);
+
+    /**
+     * Invoked when the payload has been applied, whether successfully or unsuccessfully. The value
+     * of {@code errorCode} will be one of the values from {@link UpdateEngine.ErrorCodeConstants}.
+     *
+     * @hide
+     */
+    public abstract void onPayloadApplicationComplete(@UpdateEngineStable.ErrorCode int errorCode);
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 54cc5f4..84fddcb 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7428,6 +7428,20 @@
         public static final String DEFAULT_INPUT_METHOD = "default_input_method";
 
         /**
+         * Used only by {@link com.android.server.inputmethod.InputMethodManagerService} as a
+         * temporary data store of {@link #DEFAULT_INPUT_METHOD} while a virtual-device-specific
+         * input method is set as default.</p>
+         *
+         * <p>This should be considered to be an implementation detail of
+         * {@link com.android.server.inputmethod.InputMethodManagerService}.  Other system
+         * components should never rely on this value.</p>
+         *
+         * @see #DEFAULT_INPUT_METHOD
+         * @hide
+         */
+        public static final String DEFAULT_DEVICE_INPUT_METHOD = "default_device_input_method";
+
+        /**
          * Setting to record the input method subtype used by default, holding the ID
          * of the desired method.
          */
@@ -14347,6 +14361,19 @@
                 "mute_alarm_stream_with_ringer_mode";
 
         /**
+         * The user's choice for whether or not Alarm stream should always be muted with Ringer.
+         *
+         * <p>Note that this is different from {@link #MUTE_ALARM_STREAM_WITH_RINGER_MODE}, which
+         * controls the real state of whether or not the Alarm stream and Ringer association occurs.
+         * The two Settings are not necessarily equal, if the final decision for the association
+         * depends on factors beyond the user's preference.
+         *
+         * @hide
+         */
+        public static final String MUTE_ALARM_STREAM_WITH_RINGER_MODE_USER_PREFERENCE =
+                "mute_alarm_stream_with_ringer_mode_user_preference";
+
+        /**
          * Overlay display devices setting.
          * The associated value is a specially formatted string that describes the
          * size and density of simulated secondary display devices.
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index e4af2da..d47ff2e 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -368,11 +368,13 @@
          * <p>
          * As of Android 11 apps will need specific permission to query other packages. To use
          * this method an app must include in their AndroidManifest:
+         * <pre>{@code
          * <queries>
          *   <intent>
          *     <action android:name="android.provider.Telephony.SMS_DELIVER"/>
          *   </intent>
          * </queries>
+         * }</pre>
          * Which will allow them to query packages which declare intent filters that include
          * the {@link android.provider.Telephony.Sms.Intents#SMS_DELIVER_ACTION} intent.
          * </p>
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 75be729..2b99e1e 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -22856,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.
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/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
index 0cc19fb..48f8f1b 100644
--- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
+++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
@@ -24,6 +24,13 @@
 }
 
 flag {
+    namespace: "accessibility"
+    name: "braille_display_hid"
+    description: "Enables new APIs for an AccessibilityService to communicate with a HID Braille display"
+    bug: "303522222"
+}
+
+flag {
     name: "cleanup_accessibility_warning_dialog"
     namespace: "accessibility"
     description: "Cleans up duplicated or broken logic surrounding the accessibility warning dialog."
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 216acdc..3366a7e 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -38,6 +38,14 @@
 }
 
 flag {
+  name: "draw_magnifier_border_outside_wmlock"
+  namespace: "windowing_frontend"
+  description: "Avoid holding WM locks for a long time when executing lockCanvas"
+  bug: "316075123"
+  is_fixed_read_only: true
+}
+
+flag {
   name: "introduce_smoother_dimmer"
   namespace: "windowing_frontend"
   description: "Refactor dim to fix flickers"
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/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/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 65597de..066f38b 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -819,14 +819,20 @@
             Log.e(TAG, "onTaskFragmentParentInfoChanged on empty Task id=" + taskId);
             return;
         }
+        // Checks if container should be updated before apply new parentInfo.
+        final boolean shouldUpdateContainer = taskContainer.shouldUpdateContainer(parentInfo);
         taskContainer.updateTaskFragmentParentInfo(parentInfo);
         if (!taskContainer.isVisible()) {
             // Don't update containers if the task is not visible. We only update containers when
             // parentInfo#isVisibleRequested is true.
             return;
         }
-        if (isInPictureInPicture(parentInfo.getConfiguration())) {
-            // No need to update presentation in PIP until the Task exit PIP.
+
+        // If the last direct activity of the host task is dismissed and the overlay container is
+        // the only taskFragment, the overlay container should also be dismissed.
+        dismissOverlayContainerIfNeeded(wct, taskContainer);
+
+        if (!shouldUpdateContainer) {
             return;
         }
         updateContainersInTask(wct, taskContainer);
@@ -1947,11 +1953,8 @@
     void updateOverlayContainer(@NonNull WindowContainerTransaction wct,
             @NonNull TaskFragmentContainer container) {
         final TaskContainer taskContainer = container.getTaskContainer();
-        // Dismiss the overlay container if it's the only container in the task and there's no
-        // direct activity in the parent task.
-        if (taskContainer.getTaskFragmentContainers().size() == 1
-                && !taskContainer.hasDirectActivity()) {
-            container.finish(false /* shouldFinishDependent */, mPresenter, wct, this);
+
+        if (dismissOverlayContainerIfNeeded(wct, taskContainer)) {
             return;
         }
 
@@ -1968,6 +1971,24 @@
         }
     }
 
+    /** Dismisses the overlay container in the {@code taskContainer} if needed. */
+    @GuardedBy("mLock")
+    private boolean dismissOverlayContainerIfNeeded(@NonNull WindowContainerTransaction wct,
+            @NonNull TaskContainer taskContainer) {
+        final TaskFragmentContainer overlayContainer = taskContainer.getOverlayContainer();
+        if (overlayContainer == null) {
+            return false;
+        }
+        // Dismiss the overlay container if it's the only container in the task and there's no
+        // direct activity in the parent task.
+        if (taskContainer.getTaskFragmentContainers().size() == 1
+                && !taskContainer.hasDirectActivity()) {
+            mPresenter.cleanupContainer(wct, overlayContainer, false /* shouldFinishDependant */);
+            return true;
+        }
+        return false;
+    }
+
     /**
      * Updates {@link SplitContainer} with the given {@link SplitAttributes} if the
      * {@link SplitContainer} is the top most and not finished. If passed {@link SplitAttributes}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
index 64ad4fa..71195b6 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
@@ -138,6 +138,21 @@
     }
 
     /**
+     * Returns {@code true} if the container should be updated with {@code info}.
+     */
+    boolean shouldUpdateContainer(@NonNull TaskFragmentParentInfo info) {
+        final Configuration configuration = info.getConfiguration();
+
+        return info.isVisible()
+                // No need to update presentation in PIP until the Task exit PIP.
+                && !isInPictureInPicture(configuration)
+                // If the task properties equals regardless of starting position, don't need to
+                // update the container.
+                && (mConfiguration.diffPublicOnly(configuration) != 0
+                || mDisplayId != info.getDisplayId());
+    }
+
+    /**
      * Returns the windowing mode for the TaskFragments below this Task, which should be split with
      * other TaskFragments.
      *
@@ -161,7 +176,11 @@
     }
 
     boolean isInPictureInPicture() {
-        return getWindowingMode() == WINDOWING_MODE_PINNED;
+        return isInPictureInPicture(mConfiguration);
+    }
+
+    private static boolean isInPictureInPicture(@NonNull Configuration configuration) {
+        return configuration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_PINNED;
     }
 
     boolean isInMultiWindow() {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index b52971a..6fe8e50 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -616,6 +616,9 @@
      * Removes all activities that belong to this process and finishes other containers/activities
      * configured to finish together.
      */
+    // Suppress GuardedBy warning because lint ask to mark this method as
+    // @GuardedBy(container.mController.mLock), which is mLock itself
+    @SuppressWarnings("GuardedBy")
     @GuardedBy("mController.mLock")
     void finish(boolean shouldFinishDependent, @NonNull SplitPresenter presenter,
             @NonNull WindowContainerTransaction wct, @NonNull SplitController controller) {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TransactionManager.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TransactionManager.java
index 396956e..6624c70 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TransactionManager.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TransactionManager.java
@@ -77,9 +77,11 @@
     @NonNull
     TransactionRecord startNewTransaction(@Nullable IBinder taskFragmentTransactionToken) {
         if (mCurrentTransaction != null) {
+            final TransactionRecord lastTransaction = mCurrentTransaction;
             mCurrentTransaction = null;
             throw new IllegalStateException(
-                    "The previous transaction has not been applied or aborted,");
+                    "The previous transaction:" + lastTransaction + " has not been applied or "
+                            + "aborted.");
         }
         mCurrentTransaction = new TransactionRecord(taskFragmentTransactionToken);
         return mCurrentTransaction;
@@ -199,5 +201,15 @@
                     ? mOriginType
                     : TASK_FRAGMENT_TRANSIT_CHANGE;
         }
+
+        @Override
+        @NonNull
+        public String toString() {
+            return TransactionRecord.class.getSimpleName() + "{"
+                    + "token=" + mTaskFragmentTransactionToken
+                    + ", type=" + getTransactionTransitionType()
+                    + ", transaction=" + mTransaction
+                    + "}";
+        }
     }
 }
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
index 5ef6a52..bc92101 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
@@ -472,6 +472,29 @@
         verify(mSplitPresenter).applyActivityStackAttributes(any(), eq(container), eq(attrs));
     }
 
+    @Test
+    public void testOnTaskFragmentParentInfoChanged_positionOnlyChange_earlyReturn() {
+        final TaskFragmentContainer overlayContainer = createTestOverlayContainer(TASK_ID, "test");
+
+        final TaskContainer taskContainer = overlayContainer.getTaskContainer();
+        spyOn(taskContainer);
+        final TaskContainer.TaskProperties taskProperties = taskContainer.getTaskProperties();
+        final TaskFragmentParentInfo parentInfo = new TaskFragmentParentInfo(
+                new Configuration(taskProperties.getConfiguration()), taskProperties.getDisplayId(),
+                true /* visible */, false /* hasDirectActivity */, null /* decorSurface */);
+        parentInfo.getConfiguration().windowConfiguration.getBounds().offset(10, 10);
+
+        mSplitController.onTaskFragmentParentInfoChanged(mTransaction, TASK_ID, parentInfo);
+
+        // The parent info must be applied to the task container
+        verify(taskContainer).updateTaskFragmentParentInfo(parentInfo);
+        verify(mSplitController, never()).updateContainer(any(), any());
+
+        assertWithMessage("The overlay container must still be dismissed even if "
+                + "#updateContainer is not called")
+                .that(taskContainer.getOverlayContainer()).isNull();
+    }
+
     /**
      * A simplified version of {@link SplitController.ActivityStartMonitor
      * #createOrUpdateOverlayTaskFragmentIfNeeded}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
index 6213f62..8f04f12 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
@@ -63,6 +63,10 @@
         if ((touchX < mStartThresholdX && mSwipeEdge == BackEvent.EDGE_LEFT)
                 || (touchX > mStartThresholdX && mSwipeEdge == BackEvent.EDGE_RIGHT)) {
             mStartThresholdX = touchX;
+            if ((mSwipeEdge == BackEvent.EDGE_LEFT && mStartThresholdX < mInitTouchX)
+                    || (mSwipeEdge == BackEvent.EDGE_RIGHT && mStartThresholdX > mInitTouchX)) {
+                mInitTouchX = mStartThresholdX;
+            }
         }
         mLatestTouchX = touchX;
         mLatestTouchY = touchY;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.kt
index bf07dcc..6dbb1e2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.kt
@@ -170,6 +170,71 @@
         nonLinearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / nonLinearTarget)
     }
 
+    @Test
+    fun restartingGesture_resetsInitialTouchX_leftEdge() {
+        val linearTracker = linearTouchTracker()
+        linearTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0f, BackEvent.EDGE_LEFT)
+        var touchX = 100f
+        val velocityX = 0f
+        val velocityY = 0f
+
+        // assert that progress is increased when increasing touchX
+        linearTracker.update(touchX, 0f, velocityX, velocityY)
+        linearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / MAX_DISTANCE)
+
+        // assert that progress is reset to 0 when start location is updated
+        linearTracker.updateStartLocation()
+        linearTracker.assertProgress(0f)
+
+        // assert that progress remains 0 when touchX is decreased
+        touchX -= 50
+        linearTracker.update(touchX, 0f, velocityX, velocityY)
+        linearTracker.assertProgress(0f)
+
+        // assert that progress uses new minimal touchX for progress calculation
+        val newInitialTouchX = touchX
+        touchX += 100
+        linearTracker.update(touchX, 0f, velocityX, velocityY)
+        linearTracker.assertProgress((touchX - newInitialTouchX) / MAX_DISTANCE)
+
+        // assert the same for triggerBack==true
+        linearTracker.triggerBack = true
+        linearTracker.assertProgress((touchX - newInitialTouchX) / MAX_DISTANCE)
+    }
+
+    @Test
+    fun restartingGesture_resetsInitialTouchX_rightEdge() {
+        val linearTracker = linearTouchTracker()
+        linearTracker.setGestureStartLocation(INITIAL_X_RIGHT_EDGE, 0f, BackEvent.EDGE_RIGHT)
+
+        var touchX = INITIAL_X_RIGHT_EDGE - 100f
+        val velocityX = 0f
+        val velocityY = 0f
+
+        // assert that progress is increased when decreasing touchX
+        linearTracker.update(touchX, 0f, velocityX, velocityY)
+        linearTracker.assertProgress((INITIAL_X_RIGHT_EDGE - touchX) / MAX_DISTANCE)
+
+        // assert that progress is reset to 0 when start location is updated
+        linearTracker.updateStartLocation()
+        linearTracker.assertProgress(0f)
+
+        // assert that progress remains 0 when touchX is increased
+        touchX += 50
+        linearTracker.update(touchX, 0f, velocityX, velocityY)
+        linearTracker.assertProgress(0f)
+
+        // assert that progress uses new maximal touchX for progress calculation
+        val newInitialTouchX = touchX
+        touchX -= 100
+        linearTracker.update(touchX, 0f, velocityX, velocityY)
+        linearTracker.assertProgress((newInitialTouchX - touchX) / MAX_DISTANCE)
+
+        // assert the same for triggerBack==true
+        linearTracker.triggerBack = true
+        linearTracker.assertProgress((newInitialTouchX - touchX) / MAX_DISTANCE)
+    }
+
     companion object {
         private const val MAX_DISTANCE = 500f
         private const val LINEAR_DISTANCE = 400f
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/media/java/android/media/LoudnessCodecConfigurator.java b/media/java/android/media/LoudnessCodecConfigurator.java
index de9d87c0..aadd783 100644
--- a/media/java/android/media/LoudnessCodecConfigurator.java
+++ b/media/java/android/media/LoudnessCodecConfigurator.java
@@ -234,19 +234,21 @@
      * @param mediaCodec the codec to start receiving asynchronous loudness
      *                   updates. The codec has to be in a configured or started
      *                   state in order to add it for loudness updates.
-     * @throws IllegalArgumentException if the {@code mediaCodec} was not configured,
-     *                                  does not contain loudness metadata or if it
-     *                                  was already added before
+     * @throws IllegalArgumentException if the same {@code mediaCodec} was already
+     *                                  added before.
+     * @return {@code false} if the {@code mediaCodec} was not configured or does
+     *         not contain loudness metadata, {@code true} otherwise.
      */
     @FlaggedApi(FLAG_LOUDNESS_CONFIGURATOR_API)
-    public void addMediaCodec(@NonNull MediaCodec mediaCodec) {
+    public boolean addMediaCodec(@NonNull MediaCodec mediaCodec) {
         final MediaCodec mc = Objects.requireNonNull(mediaCodec,
                 "MediaCodec for addMediaCodec cannot be null");
         int piid = PLAYER_PIID_INVALID;
         final LoudnessCodecInfo mcInfo = getCodecInfo(mc);
 
         if (mcInfo == null) {
-            throw new IllegalArgumentException("Could not extract codec loudness information");
+            Log.v(TAG, "Could not extract codec loudness information");
+            return false;
         }
         synchronized (mConfiguratorLock) {
             final AtomicBoolean containsCodec = new AtomicBoolean(false);
@@ -271,6 +273,8 @@
         if (piid != PLAYER_PIID_INVALID) {
             mLcDispatcher.addLoudnessCodecInfo(piid, mediaCodec.hashCode(), mcInfo);
         }
+
+        return true;
     }
 
     /**
diff --git a/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java b/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java
index ce1004c..74e5612 100644
--- a/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java
+++ b/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java
@@ -19,6 +19,7 @@
 import static android.media.audio.Flags.FLAG_LOUDNESS_CONFIGURATOR_API;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
@@ -191,6 +192,18 @@
 
     @Test
     @RequiresFlagsEnabled(FLAG_LOUDNESS_CONFIGURATOR_API)
+    public void addUnconfiguredMediaCodec_returnsFalse() throws Exception {
+        final MediaCodec mediaCodec = MediaCodec.createDecoderByType("audio/mpeg");
+
+        try {
+            assertFalse(mLcc.addMediaCodec(mediaCodec));
+        } finally {
+            mediaCodec.release();
+        }
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_LOUDNESS_CONFIGURATOR_API)
     public void setClearTrack_removeAllAudioServicePiidCodecs() throws Exception {
         final ArgumentCaptor<List> argument = ArgumentCaptor.forClass(List.class);
         final AudioTrack track = createAudioTrack();
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/res/drawable/more_horiz_24px.xml b/packages/CredentialManager/res/drawable/more_horiz_24px.xml
new file mode 100644
index 0000000..7b235f8
--- /dev/null
+++ b/packages/CredentialManager/res/drawable/more_horiz_24px.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="960"
+    android:viewportHeight="960"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M240,560Q207,560 183.5,536.5Q160,513 160,480Q160,447 183.5,423.5Q207,400 240,400Q273,400 296.5,423.5Q320,447 320,480Q320,513 296.5,536.5Q273,560 240,560ZM480,560Q447,560 423.5,536.5Q400,513 400,480Q400,447 423.5,423.5Q447,400 480,400Q513,400 536.5,423.5Q560,447 560,480Q560,513 536.5,536.5Q513,560 480,560ZM720,560Q687,560 663.5,536.5Q640,513 640,480Q640,447 663.5,423.5Q687,400 720,400Q753,400 776.5,423.5Q800,447 800,480Q800,513 776.5,536.5Q753,560 720,560Z"/>
+</vector>
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
index dfa5735..34f2509 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -398,7 +398,7 @@
         val sliceBuilder = InlineSuggestionUi
                 .newContentBuilder(bottomSheetPendingIntent)
                 .setStartIcon(Icon.createWithResource(this,
-                        com.android.credentialmanager.R.drawable.ic_other_sign_in_24))
+                        com.android.credentialmanager.R.drawable.more_horiz_24px))
         val presentationBuilder = Presentations.Builder()
                 .setInlinePresentation(InlinePresentation(
                         sliceBuilder.build().slice, spec, /* pinned= */ true))
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/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
index f4edb36..460a6f7 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
@@ -24,7 +24,8 @@
 import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider
 import com.android.settingslib.spa.gallery.card.CardPageProvider
 import com.android.settingslib.spa.gallery.chart.ChartPageProvider
-import com.android.settingslib.spa.gallery.dialog.AlertDialogPageProvider
+import com.android.settingslib.spa.gallery.dialog.DialogMainPageProvider
+import com.android.settingslib.spa.gallery.dialog.NavDialogProvider
 import com.android.settingslib.spa.gallery.editor.EditorMainPageProvider
 import com.android.settingslib.spa.gallery.editor.SettingsExposedDropdownMenuBoxPageProvider
 import com.android.settingslib.spa.gallery.editor.SettingsExposedDropdownMenuCheckBoxProvider
@@ -91,7 +92,8 @@
                 ProgressBarPageProvider,
                 LoadingBarPageProvider,
                 ChartPageProvider,
-                AlertDialogPageProvider,
+                DialogMainPageProvider,
+                NavDialogProvider,
                 ItemListPageProvider,
                 ItemOperatePageProvider,
                 OperateListPageProvider,
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/AlertDialogPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/DialogMainPageProvider.kt
similarity index 84%
rename from packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/AlertDialogPage.kt
rename to packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/DialogMainPageProvider.kt
index 1545a3e..4e3fcee 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/AlertDialogPage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/DialogMainPageProvider.kt
@@ -28,10 +28,10 @@
 import com.android.settingslib.spa.widget.preference.Preference
 import com.android.settingslib.spa.widget.preference.PreferenceModel
 
-private const val TITLE = "AlertDialogPage"
+private const val TITLE = "Category: Dialog"
 
-object AlertDialogPageProvider : SettingsPageProvider {
-    override val name = "AlertDialogPage"
+object DialogMainPageProvider : SettingsPageProvider {
+    override val name = "DialogMain"
     private val owner = createSettingsPage()
 
     override fun buildEntry(arguments: Bundle?): List<SettingsEntry> = listOf(
@@ -47,6 +47,12 @@
                 override val onClick = alertDialogPresenter::open
             })
         }.build(),
+        SettingsEntryBuilder.create("NavDialog", owner).setUiLayoutFn {
+            Preference(object : PreferenceModel {
+                override val title = "Navigate to Dialog"
+                override val onClick = navigator(route = NavDialogProvider.name)
+            })
+        }.build(),
     )
 
     fun buildInjectEntry() = SettingsEntryBuilder.createInject(owner)
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/NavDialogProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/NavDialogProvider.kt
new file mode 100644
index 0000000..6f79911
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/dialog/NavDialogProvider.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.gallery.dialog
+
+import android.os.Bundle
+import androidx.compose.runtime.Composable
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.widget.dialog.SettingsDialogCard
+
+object NavDialogProvider : SettingsPageProvider {
+    override val name = "NavDialog"
+    override val navType = SettingsPageProvider.NavType.Dialog
+
+    @Composable
+    override fun Page(arguments: Bundle?) {
+        SettingsDialogCard("Example Nav Dialog") {}
+    }
+}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
index 6a2e598..1f028d5 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
@@ -30,7 +30,7 @@
 import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider
 import com.android.settingslib.spa.gallery.card.CardPageProvider
 import com.android.settingslib.spa.gallery.chart.ChartPageProvider
-import com.android.settingslib.spa.gallery.dialog.AlertDialogPageProvider
+import com.android.settingslib.spa.gallery.dialog.DialogMainPageProvider
 import com.android.settingslib.spa.gallery.editor.EditorMainPageProvider
 import com.android.settingslib.spa.gallery.itemList.OperateListPageProvider
 import com.android.settingslib.spa.gallery.page.ArgumentPageModel
@@ -71,7 +71,7 @@
             ProgressBarPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
             LoadingBarPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
             ChartPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
-            AlertDialogPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
+            DialogMainPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
             EditorMainPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
             CardPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
             CopyablePageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
index 9f8c868..5605485 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
@@ -22,7 +22,6 @@
 import androidx.activity.ComponentActivity
 import androidx.activity.compose.setContent
 import androidx.annotation.VisibleForTesting
-import androidx.compose.foundation.layout.Box
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.LaunchedEffect
@@ -30,15 +29,19 @@
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.core.view.WindowCompat
+import androidx.navigation.NavBackStackEntry
 import androidx.navigation.NavGraph.Companion.findStartDestination
+import androidx.navigation.NavGraphBuilder
 import androidx.navigation.compose.NavHost
 import androidx.navigation.compose.composable
+import androidx.navigation.compose.dialog
 import androidx.navigation.compose.rememberNavController
 import com.android.settingslib.spa.R
 import com.android.settingslib.spa.framework.common.LogCategory
 import com.android.settingslib.spa.framework.common.NullPageProvider
 import com.android.settingslib.spa.framework.common.SettingsPage
 import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.common.SettingsPageProvider.NavType
 import com.android.settingslib.spa.framework.common.SettingsPageProviderRepository
 import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
 import com.android.settingslib.spa.framework.common.createSettingsPage
@@ -127,27 +130,31 @@
     allProvider: Collection<SettingsPageProvider>,
     content: @Composable (SettingsPage) -> Unit,
 ) {
-    // TODO(b/298520326): Remove Box after the issue is fixed.
-    // Wrap the top level node into a Box to workaround an issue of Compose 1.6.0-alpha03.
-    Box {
-        NavHost(
-            navController = navController,
-            startDestination = NullPageProvider.name,
-        ) {
-            composable(NullPageProvider.name) {}
-            for (spp in allProvider) {
-                animatedComposable(
-                    route = spp.name + spp.parameter.navRoute(),
-                    arguments = spp.parameter,
-                ) { navBackStackEntry ->
-                    val page = remember { spp.createSettingsPage(navBackStackEntry.arguments) }
-                    content(page)
-                }
+    NavHost(
+        navController = navController,
+        startDestination = NullPageProvider.name,
+    ) {
+        composable(NullPageProvider.name) {}
+        for (spp in allProvider) {
+            destination(spp) { navBackStackEntry ->
+                val page = remember { spp.createSettingsPage(navBackStackEntry.arguments) }
+                content(page)
             }
         }
     }
 }
 
+private fun NavGraphBuilder.destination(
+    spp: SettingsPageProvider,
+    content: @Composable (NavBackStackEntry) -> Unit,
+) {
+    val route = spp.name + spp.parameter.navRoute()
+    when (spp.navType) {
+        NavType.Page -> animatedComposable(route, spp.parameter) { content(it) }
+        NavType.Dialog -> dialog(route, spp.parameter) { content(it) }
+    }
+}
+
 @Composable
 private fun NavControllerWrapperImpl.InitialDestination(
     initialIntent: Intent?,
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProvider.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProvider.kt
index 18f964e..81bee5e 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProvider.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProvider.kt
@@ -34,6 +34,14 @@
     /** The page provider name, needs to be *unique* and *stable*. */
     val name: String
 
+    enum class NavType {
+        Page,
+        Dialog,
+    }
+
+    val navType: NavType
+        get() = NavType.Page
+
     /** The display name of this page provider, for better readability. */
     val displayName: String
         get() = name
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsDialog.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsDialog.kt
index 8b172da..f08e740 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsDialog.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsDialog.kt
@@ -19,10 +19,13 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.AlertDialogDefaults
 import androidx.compose.material3.Card
+import androidx.compose.material3.CardDefaults
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.window.Dialog
+import androidx.navigation.compose.NavHost
 import com.android.settingslib.spa.framework.theme.SettingsDimension
 import com.android.settingslib.spa.framework.theme.SettingsShape
 import com.android.settingslib.spa.widget.ui.SettingsTitle
@@ -34,13 +37,27 @@
     content: @Composable () -> Unit,
 ) {
     Dialog(onDismissRequest = onDismissRequest) {
-        Card(shape = SettingsShape.CornerExtraLarge) {
-            Column(modifier = Modifier.padding(vertical = SettingsDimension.itemPaddingAround)) {
-                Box(modifier = Modifier.padding(SettingsDimension.dialogItemPadding)) {
-                    SettingsTitle(title = title, useMediumWeight = true)
-                }
-                content()
+        SettingsDialogCard(title, content)
+    }
+}
+
+/**
+ * Card for dialog, suitable for independent dialog in the [NavHost].
+ */
+@Composable
+fun SettingsDialogCard(
+    title: String,
+    content: @Composable () -> Unit,
+) {
+    Card(
+        shape = SettingsShape.CornerExtraLarge,
+        colors = CardDefaults.cardColors(containerColor = AlertDialogDefaults.containerColor),
+    ) {
+        Column(modifier = Modifier.padding(vertical = SettingsDimension.itemPaddingAround)) {
+            Box(modifier = Modifier.padding(SettingsDimension.dialogItemPadding)) {
+                SettingsTitle(title = title, useMediumWeight = true)
             }
+            content()
         }
     }
 }
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/BrowseActivityTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/BrowseActivityTest.kt
index 92d3411..8cbd964 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/BrowseActivityTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/BrowseActivityTest.kt
@@ -18,6 +18,7 @@
 
 import android.content.Context
 import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.hasText
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onAllNodesWithText
 import androidx.compose.ui.test.onNodeWithText
@@ -29,12 +30,14 @@
 import com.android.settingslib.spa.framework.common.SettingsPage
 import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
 import com.android.settingslib.spa.framework.common.createSettingsPage
+import com.android.settingslib.spa.tests.testutils.SppDialog
 import com.android.settingslib.spa.tests.testutils.SpaEnvironmentForTest
 import com.android.settingslib.spa.tests.testutils.SpaLoggerForTest
 import com.android.settingslib.spa.tests.testutils.SppDisabled
 import com.android.settingslib.spa.tests.testutils.SppHome
 import com.android.settingslib.spa.testutils.waitUntil
-import com.google.common.truth.Truth
+import com.android.settingslib.spa.testutils.waitUntilExists
+import com.google.common.truth.Truth.assertThat
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -106,11 +109,31 @@
         composeTestRule.onNodeWithText(sppDisabled.getTitle(null)).assertDoesNotExist()
         spaLogger.verifyPageEvent(pageDisabled.id, 0, 0)
     }
+
+    @Test
+    fun browseContent_dialog() {
+        val spaEnvironment = SpaEnvironmentForTest(
+            context = context,
+            rootPages = listOf(SppHome.createSettingsPage()),
+            logger = spaLogger,
+        )
+        SpaEnvironmentFactory.reset(spaEnvironment)
+        val sppRepository by spaEnvironment.pageProviderRepository
+
+        composeTestRule.setContent {
+            BrowseContent(
+                sppRepository = sppRepository,
+                isPageEnabled = SettingsPage::isEnabled,
+                initialIntent = null,
+            )
+        }
+        composeTestRule.onNodeWithText(SppDialog.name).performClick()
+
+        composeTestRule.waitUntilExists(hasText(SppDialog.CONTENT))
+    }
 }
 
 private fun SpaLoggerForTest.verifyPageEvent(id: String, entryCount: Int, leaveCount: Int) {
-    Truth.assertThat(getEventCount(id, LogEvent.PAGE_ENTER, LogCategory.FRAMEWORK))
-        .isEqualTo(entryCount)
-    Truth.assertThat(getEventCount(id, LogEvent.PAGE_LEAVE, LogCategory.FRAMEWORK))
-        .isEqualTo(leaveCount)
+    assertThat(getEventCount(id, LogEvent.PAGE_ENTER, LogCategory.FRAMEWORK)).isEqualTo(entryCount)
+    assertThat(getEventCount(id, LogEvent.PAGE_LEAVE, LogCategory.FRAMEWORK)).isEqualTo(leaveCount)
 }
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsEntryRepositoryTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsEntryRepositoryTest.kt
index b139f28..0a1c05f 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsEntryRepositoryTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsEntryRepositoryTest.kt
@@ -22,6 +22,7 @@
 import com.android.settingslib.spa.framework.util.genEntryId
 import com.android.settingslib.spa.framework.util.genPageId
 import com.android.settingslib.spa.tests.testutils.SpaEnvironmentForTest
+import com.android.settingslib.spa.tests.testutils.SppDialog
 import com.android.settingslib.spa.tests.testutils.SppHome
 import com.android.settingslib.spa.tests.testutils.SppLayer1
 import com.android.settingslib.spa.tests.testutils.SppLayer2
@@ -39,26 +40,21 @@
     @Test
     fun testGetPageWithEntry() {
         val pageWithEntry = entryRepository.getAllPageWithEntry()
-        assertThat(pageWithEntry.size).isEqualTo(3)
-        assertThat(
-            entryRepository.getPageWithEntry(genPageId("SppHome"))
-                ?.entries?.size
-        ).isEqualTo(1)
-        assertThat(
-            entryRepository.getPageWithEntry(genPageId("SppLayer1"))
-                ?.entries?.size
-        ).isEqualTo(3)
-        assertThat(
-            entryRepository.getPageWithEntry(genPageId("SppLayer2"))
-                ?.entries?.size
-        ).isEqualTo(2)
+
+        assertThat(pageWithEntry).hasSize(4)
+        assertThat(entryRepository.getPageWithEntry(genPageId("SppHome"))?.entries)
+            .hasSize(2)
+        assertThat(entryRepository.getPageWithEntry(genPageId("SppLayer1"))?.entries)
+            .hasSize(3)
+        assertThat(entryRepository.getPageWithEntry(genPageId("SppLayer2"))?.entries)
+            .hasSize(2)
         assertThat(entryRepository.getPageWithEntry(genPageId("SppWithParam"))).isNull()
     }
 
     @Test
     fun testGetEntry() {
         val entry = entryRepository.getAllEntries()
-        assertThat(entry.size).isEqualTo(7)
+        assertThat(entry).hasSize(8)
         assertThat(
             entryRepository.getEntry(
                 genEntryId(
@@ -91,6 +87,16 @@
         ).isNotNull()
         assertThat(
             entryRepository.getEntry(
+                genEntryId(
+                    "INJECT",
+                    SppDialog.createSettingsPage(),
+                    SppHome.createSettingsPage(),
+                    SppDialog.createSettingsPage(),
+                )
+            )
+        ).isNotNull()
+        assertThat(
+            entryRepository.getEntry(
                 genEntryId("Layer1Entry1", SppLayer1.createSettingsPage())
             )
         ).isNotNull()
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsPageProviderRepositoryTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsPageProviderRepositoryTest.kt
index 8576573..169c541 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsPageProviderRepositoryTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/common/SettingsPageProviderRepositoryTest.kt
@@ -25,39 +25,55 @@
 @RunWith(AndroidJUnit4::class)
 class SettingsPageProviderRepositoryTest {
     @Test
-    fun getStartPageTest() {
-        val sppRepoEmpty = SettingsPageProviderRepository(emptyList(), emptyList())
+    fun rootPages_empty() {
+        val sppRepoEmpty = SettingsPageProviderRepository(emptyList())
+
         assertThat(sppRepoEmpty.getDefaultStartPage()).isEqualTo("")
         assertThat(sppRepoEmpty.getAllRootPages()).isEmpty()
-
-        val nullPage = NullPageProvider.createSettingsPage()
-        val sppRepoNull =
-            SettingsPageProviderRepository(emptyList(), listOf(nullPage))
-        assertThat(sppRepoNull.getDefaultStartPage()).isEqualTo("NULL")
-        assertThat(sppRepoNull.getAllRootPages()).contains(nullPage)
-
-        val rootPage1 = createSettingsPage(sppName = "Spp1", displayName = "Spp1")
-        val rootPage2 = createSettingsPage(sppName = "Spp2", displayName = "Spp2")
-        val sppRepo = SettingsPageProviderRepository(emptyList(), listOf(rootPage1, rootPage2))
-        val allRoots = sppRepo.getAllRootPages()
-        assertThat(sppRepo.getDefaultStartPage()).isEqualTo("Spp1")
-        assertThat(allRoots.size).isEqualTo(2)
-        assertThat(allRoots).contains(rootPage1)
-        assertThat(allRoots).contains(rootPage2)
     }
 
     @Test
-    fun getProviderTest() {
-        val sppRepoEmpty = SettingsPageProviderRepository(emptyList(), emptyList())
+    fun rootPages_single() {
+        val nullPage = NullPageProvider.createSettingsPage()
+
+        val sppRepoNull = SettingsPageProviderRepository(
+            allPageProviders = emptyList(),
+            rootPages = listOf(nullPage),
+        )
+
+        assertThat(sppRepoNull.getDefaultStartPage()).isEqualTo("NULL")
+        assertThat(sppRepoNull.getAllRootPages()).containsExactly(nullPage)
+    }
+
+    @Test
+    fun rootPages_twoPages() {
+        val rootPage1 = createSettingsPage(sppName = "Spp1", displayName = "Spp1")
+        val rootPage2 = createSettingsPage(sppName = "Spp2", displayName = "Spp2")
+
+        val sppRepo = SettingsPageProviderRepository(
+            allPageProviders = emptyList(),
+            rootPages = listOf(rootPage1, rootPage2),
+        )
+
+        assertThat(sppRepo.getDefaultStartPage()).isEqualTo("Spp1")
+        assertThat(sppRepo.getAllRootPages()).containsExactly(rootPage1, rootPage2)
+    }
+
+    @Test
+    fun getProviderOrNull_empty() {
+        val sppRepoEmpty = SettingsPageProviderRepository(emptyList())
         assertThat(sppRepoEmpty.getAllProviders()).isEmpty()
         assertThat(sppRepoEmpty.getProviderOrNull("Spp")).isNull()
+    }
 
+    @Test
+    fun getProviderOrNull_single() {
         val sppRepo = SettingsPageProviderRepository(listOf(
             object : SettingsPageProvider {
                 override val name = "Spp"
             }
-        ), emptyList())
-        assertThat(sppRepo.getAllProviders().size).isEqualTo(1)
+        ))
+        assertThat(sppRepo.getAllProviders()).hasSize(1)
         assertThat(sppRepo.getProviderOrNull("Spp")).isNotNull()
         assertThat(sppRepo.getProviderOrNull("SppUnknown")).isNull()
     }
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/tests/testutils/SpaEnvironmentForTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/tests/testutils/SpaEnvironmentForTest.kt
index 2755b4e..22a5ca3 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/tests/testutils/SpaEnvironmentForTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/tests/testutils/SpaEnvironmentForTest.kt
@@ -21,6 +21,8 @@
 import android.content.Context
 import android.content.Intent
 import android.os.Bundle
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
 import androidx.navigation.NavType
 import androidx.navigation.navArgument
 import com.android.settingslib.spa.framework.BrowseActivity
@@ -88,6 +90,7 @@
         val owner = this.createSettingsPage()
         return listOf(
             SppLayer1.buildInject().setLink(fromPage = owner).build(),
+            SppDialog.buildInject().setLink(fromPage = owner).build(),
         )
     }
 }
@@ -160,6 +163,21 @@
     }
 }
 
+object SppDialog : SettingsPageProvider {
+    override val name = "SppDialog"
+    override val navType = SettingsPageProvider.NavType.Dialog
+
+    const val CONTENT = "SppDialog Content"
+
+    @Composable
+    override fun Page(arguments: Bundle?) {
+        Text(CONTENT)
+    }
+
+    fun buildInject() = SettingsEntryBuilder.createInject(this.createSettingsPage())
+        .setMacro { SimplePreferenceMacro(title = name, clickRoute = name) }
+}
+
 object SppForSearch : SettingsPageProvider {
     override val name = "SppForSearch"
 
@@ -223,6 +241,7 @@
                         navArgument("rt_param") { type = NavType.StringType },
                     )
                 },
+                SppDialog,
             ),
             rootPages
         )
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/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
index d6e8d26..2e39adc 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
@@ -69,6 +69,7 @@
         Settings.Global.PRIVATE_DNS_SPECIFIER,
         Settings.Global.SOFT_AP_TIMEOUT_ENABLED,
         Settings.Global.ZEN_DURATION,
+        Settings.Global.MUTE_ALARM_STREAM_WITH_RINGER_MODE_USER_PREFERENCE,
         Settings.Global.REVERSE_CHARGING_AUTO_ON,
         Settings.Global.CHARGING_VIBRATION_ENABLED,
         Settings.Global.AWARE_ALLOWED,
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index 59c3cd3..e7d7bb0 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -19,93 +19,105 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.provider.Settings;
 
+import com.android.server.display.feature.flags.Flags;
+
+import java.util.ArrayList;
+import java.util.List;
+
 /** Information about the system settings to back up */
 public class SystemSettings {
 
     /**
-     * Settings to backup.
+     * Settings to back up.
      *
      * NOTE: Settings are backed up and restored in the order they appear
      *       in this array. If you have one setting depending on another,
      *       make sure that they are ordered appropriately.
      */
     @UnsupportedAppUsage
-    public static final String[] SETTINGS_TO_BACKUP = {
-        Settings.System.STAY_ON_WHILE_PLUGGED_IN,   // moved to global
-        Settings.System.WIFI_USE_STATIC_IP,
-        Settings.System.WIFI_STATIC_IP,
-        Settings.System.WIFI_STATIC_GATEWAY,
-        Settings.System.WIFI_STATIC_NETMASK,
-        Settings.System.WIFI_STATIC_DNS1,
-        Settings.System.WIFI_STATIC_DNS2,
-        Settings.System.BLUETOOTH_DISCOVERABILITY,
-        Settings.System.BLUETOOTH_DISCOVERABILITY_TIMEOUT,
-        Settings.System.FONT_SCALE,
-        Settings.System.DIM_SCREEN,
-        Settings.System.SCREEN_OFF_TIMEOUT,
-        Settings.System.SCREEN_BRIGHTNESS_MODE,
-        Settings.System.ADAPTIVE_SLEEP,             // moved to secure
-        Settings.System.APPLY_RAMPING_RINGER,
-        Settings.System.VIBRATE_INPUT_DEVICES,
-        Settings.System.MODE_RINGER_STREAMS_AFFECTED,
-        Settings.System.TEXT_AUTO_REPLACE,
-        Settings.System.TEXT_AUTO_CAPS,
-        Settings.System.TEXT_AUTO_PUNCTUATE,
-        Settings.System.TEXT_SHOW_PASSWORD,
-        Settings.System.AUTO_TIME,                  // moved to global
-        Settings.System.AUTO_TIME_ZONE,             // moved to global
-        Settings.System.TIME_12_24,
-        Settings.System.DTMF_TONE_WHEN_DIALING,
-        Settings.System.DTMF_TONE_TYPE_WHEN_DIALING,
-        Settings.System.HEARING_AID,
-        Settings.System.TTY_MODE,
-        Settings.System.MASTER_MONO,
-        Settings.System.MASTER_BALANCE,
-        Settings.System.FOLD_LOCK_BEHAVIOR,
-        Settings.System.SOUND_EFFECTS_ENABLED,
-        Settings.System.HAPTIC_FEEDBACK_ENABLED,
-        Settings.System.POWER_SOUNDS_ENABLED,       // moved to global
-        Settings.System.DOCK_SOUNDS_ENABLED,        // moved to global
-        Settings.System.LOCKSCREEN_SOUNDS_ENABLED,
-        Settings.System.SHOW_WEB_SUGGESTIONS,
-        Settings.System.SIP_CALL_OPTIONS,
-        Settings.System.SIP_RECEIVE_CALLS,
-        Settings.System.POINTER_SPEED,
-        Settings.System.VIBRATE_ON,
-        Settings.System.VIBRATE_WHEN_RINGING,
-        Settings.System.RINGTONE,
-        Settings.System.LOCK_TO_APP_ENABLED,
-        Settings.System.NOTIFICATION_SOUND,
-        Settings.System.ACCELEROMETER_ROTATION,
-        Settings.System.SHOW_BATTERY_PERCENT,
-        Settings.System.ALARM_VIBRATION_INTENSITY,
-        Settings.System.MEDIA_VIBRATION_INTENSITY,
-        Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
-        Settings.System.RING_VIBRATION_INTENSITY,
-        Settings.System.HAPTIC_FEEDBACK_INTENSITY,
-        Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY,
-        Settings.System.KEYBOARD_VIBRATION_ENABLED,
-        Settings.System.HAPTIC_FEEDBACK_ENABLED,
-        Settings.System.DISPLAY_COLOR_MODE_VENDOR_HINT, // must precede DISPLAY_COLOR_MODE
-        Settings.System.DISPLAY_COLOR_MODE,
-        Settings.System.ALARM_ALERT,
-        Settings.System.NOTIFICATION_LIGHT_PULSE,
-        Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED,
-        Settings.System.CLOCKWORK_BLUETOOTH_SETTINGS_PREF,
-        Settings.System.UNREAD_NOTIFICATION_DOT_INDICATOR,
-        Settings.System.AUTO_LAUNCH_MEDIA_CONTROLS,
-        Settings.System.LOCALE_PREFERENCES,
-        Settings.System.TOUCHPAD_POINTER_SPEED,
-        Settings.System.TOUCHPAD_NATURAL_SCROLLING,
-        Settings.System.TOUCHPAD_TAP_TO_CLICK,
-        Settings.System.TOUCHPAD_RIGHT_CLICK_ZONE,
-        Settings.System.CAMERA_FLASH_NOTIFICATION,
-        Settings.System.SCREEN_FLASH_NOTIFICATION,
-        Settings.System.SCREEN_FLASH_NOTIFICATION_COLOR,
-        Settings.System.PEAK_REFRESH_RATE,
-        Settings.System.MIN_REFRESH_RATE,
-        Settings.System.NOTIFICATION_COOLDOWN_ENABLED,
-        Settings.System.NOTIFICATION_COOLDOWN_ALL,
-        Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED,
-    };
+    public static final String[] SETTINGS_TO_BACKUP = getSettingsToBackUp();
+
+    private static String[] getSettingsToBackUp() {
+        List<String> settings = new ArrayList<>(List.of(
+                Settings.System.STAY_ON_WHILE_PLUGGED_IN,   // moved to global
+                Settings.System.WIFI_USE_STATIC_IP,
+                Settings.System.WIFI_STATIC_IP,
+                Settings.System.WIFI_STATIC_GATEWAY,
+                Settings.System.WIFI_STATIC_NETMASK,
+                Settings.System.WIFI_STATIC_DNS1,
+                Settings.System.WIFI_STATIC_DNS2,
+                Settings.System.BLUETOOTH_DISCOVERABILITY,
+                Settings.System.BLUETOOTH_DISCOVERABILITY_TIMEOUT,
+                Settings.System.FONT_SCALE,
+                Settings.System.DIM_SCREEN,
+                Settings.System.SCREEN_OFF_TIMEOUT,
+                Settings.System.SCREEN_BRIGHTNESS_MODE,
+                Settings.System.ADAPTIVE_SLEEP,             // moved to secure
+                Settings.System.APPLY_RAMPING_RINGER,
+                Settings.System.VIBRATE_INPUT_DEVICES,
+                Settings.System.MODE_RINGER_STREAMS_AFFECTED,
+                Settings.System.TEXT_AUTO_REPLACE,
+                Settings.System.TEXT_AUTO_CAPS,
+                Settings.System.TEXT_AUTO_PUNCTUATE,
+                Settings.System.TEXT_SHOW_PASSWORD,
+                Settings.System.AUTO_TIME,                  // moved to global
+                Settings.System.AUTO_TIME_ZONE,             // moved to global
+                Settings.System.TIME_12_24,
+                Settings.System.DTMF_TONE_WHEN_DIALING,
+                Settings.System.DTMF_TONE_TYPE_WHEN_DIALING,
+                Settings.System.HEARING_AID,
+                Settings.System.TTY_MODE,
+                Settings.System.MASTER_MONO,
+                Settings.System.MASTER_BALANCE,
+                Settings.System.FOLD_LOCK_BEHAVIOR,
+                Settings.System.SOUND_EFFECTS_ENABLED,
+                Settings.System.HAPTIC_FEEDBACK_ENABLED,
+                Settings.System.POWER_SOUNDS_ENABLED,       // moved to global
+                Settings.System.DOCK_SOUNDS_ENABLED,        // moved to global
+                Settings.System.LOCKSCREEN_SOUNDS_ENABLED,
+                Settings.System.SHOW_WEB_SUGGESTIONS,
+                Settings.System.SIP_CALL_OPTIONS,
+                Settings.System.SIP_RECEIVE_CALLS,
+                Settings.System.POINTER_SPEED,
+                Settings.System.VIBRATE_ON,
+                Settings.System.VIBRATE_WHEN_RINGING,
+                Settings.System.RINGTONE,
+                Settings.System.LOCK_TO_APP_ENABLED,
+                Settings.System.NOTIFICATION_SOUND,
+                Settings.System.ACCELEROMETER_ROTATION,
+                Settings.System.SHOW_BATTERY_PERCENT,
+                Settings.System.ALARM_VIBRATION_INTENSITY,
+                Settings.System.MEDIA_VIBRATION_INTENSITY,
+                Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
+                Settings.System.RING_VIBRATION_INTENSITY,
+                Settings.System.HAPTIC_FEEDBACK_INTENSITY,
+                Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY,
+                Settings.System.KEYBOARD_VIBRATION_ENABLED,
+                Settings.System.HAPTIC_FEEDBACK_ENABLED,
+                Settings.System.DISPLAY_COLOR_MODE_VENDOR_HINT, // must precede DISPLAY_COLOR_MODE
+                Settings.System.DISPLAY_COLOR_MODE,
+                Settings.System.ALARM_ALERT,
+                Settings.System.NOTIFICATION_LIGHT_PULSE,
+                Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED,
+                Settings.System.CLOCKWORK_BLUETOOTH_SETTINGS_PREF,
+                Settings.System.UNREAD_NOTIFICATION_DOT_INDICATOR,
+                Settings.System.AUTO_LAUNCH_MEDIA_CONTROLS,
+                Settings.System.LOCALE_PREFERENCES,
+                Settings.System.TOUCHPAD_POINTER_SPEED,
+                Settings.System.TOUCHPAD_NATURAL_SCROLLING,
+                Settings.System.TOUCHPAD_TAP_TO_CLICK,
+                Settings.System.TOUCHPAD_RIGHT_CLICK_ZONE,
+                Settings.System.CAMERA_FLASH_NOTIFICATION,
+                Settings.System.SCREEN_FLASH_NOTIFICATION,
+                Settings.System.SCREEN_FLASH_NOTIFICATION_COLOR,
+                Settings.System.NOTIFICATION_COOLDOWN_ENABLED,
+                Settings.System.NOTIFICATION_COOLDOWN_ALL,
+                Settings.System.NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED
+        ));
+        if (Flags.backUpSmoothDisplayAndForcePeakRefreshRate()) {
+            settings.add(Settings.System.PEAK_REFRESH_RATE);
+            settings.add(Settings.System.MIN_REFRESH_RATE);
+        }
+        return settings.toArray(new String[0]);
+    }
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index f8bdcf6..5022395 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -210,6 +210,8 @@
         VALIDATORS.put(Global.POWER_BUTTON_LONG_PRESS_DURATION_MS, NONE_NEGATIVE_LONG_VALIDATOR);
         VALIDATORS.put(Global.STYLUS_EVER_USED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Global.MUTE_ALARM_STREAM_WITH_RINGER_MODE, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(
+                Global.MUTE_ALARM_STREAM_WITH_RINGER_MODE_USER_PREFERENCE, BOOLEAN_VALIDATOR);
 
         VALIDATORS.put(Global.Wearable.HAS_PAY_TOKENS, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Global.Wearable.GMS_CHECKIN_TIMEOUT_MIN, ANY_INTEGER_VALIDATOR);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 6ad10cc..1481d97 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -33,6 +33,8 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.display.feature.flags.Flags;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -53,59 +55,6 @@
     public static final String HYBRID_SYSUI_BATTERY_WARNING_FLAGS =
             "hybrid_sysui_battery_warning_flags";
 
-    /**
-     * The following denylists contain settings that should *not* be backed up and restored to
-     * another device.  As a general rule, anything that is not user configurable should be
-     * denied (and conversely, things that *are* user configurable *should* be backed up)
-     */
-    private static final Set<String> BACKUP_DENY_LIST_SYSTEM_SETTINGS =
-            newHashSet(
-                    Settings.System.ADVANCED_SETTINGS, // candidate for backup?
-                    Settings.System.ALARM_ALERT_CACHE, // internal cache
-                    Settings.System.APPEND_FOR_LAST_AUDIBLE, // suffix deprecated since API 2
-                    Settings.System.EGG_MODE, // I am the lolrus
-                    Settings.System.END_BUTTON_BEHAVIOR, // bug?
-                    Settings.System
-                            .HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, // candidate for backup?
-                    Settings.System.LOCKSCREEN_DISABLED, // ?
-                    Settings.System.MEDIA_BUTTON_RECEIVER, // candidate for backup?
-                    Settings.System.MUTE_STREAMS_AFFECTED, //  candidate for backup?
-                    Settings.System.NOTIFICATION_SOUND_CACHE, // internal cache
-                    Settings.System.POINTER_LOCATION, // backup candidate?
-                    Settings.System.DEBUG_ENABLE_ENHANCED_CALL_BLOCKING, // used for testing only
-                    Settings.System.RINGTONE_CACHE, // internal cache
-                    Settings.System.SCREEN_BRIGHTNESS, // removed in P
-                    Settings.System.SETUP_WIZARD_HAS_RUN, // Only used by SuW
-                    Settings.System.SHOW_GTALK_SERVICE_STATUS, // candidate for backup?
-                    Settings.System.SHOW_TOUCHES,
-                    Settings.System.SHOW_KEY_PRESSES,
-                    Settings.System.SHOW_ROTARY_INPUT,
-                    Settings.System.SIP_ADDRESS_ONLY, // value, not a setting
-                    Settings.System.SIP_ALWAYS, // value, not a setting
-                    Settings.System.SYSTEM_LOCALES, // bug?
-                    Settings.System.USER_ROTATION, // backup candidate?
-                    Settings.System.VIBRATE_IN_SILENT, // deprecated?
-                    Settings.System.VOLUME_ACCESSIBILITY, // used internally, changing value will
-                                                          // not change volume
-                    Settings.System.VOLUME_ALARM, // deprecated since API 2?
-                    Settings.System.VOLUME_ASSISTANT, // candidate for backup?
-                    Settings.System.VOLUME_BLUETOOTH_SCO, // deprecated since API 2?
-                    Settings.System.VOLUME_MASTER, // candidate for backup?
-                    Settings.System.VOLUME_MUSIC, // deprecated since API 2?
-                    Settings.System.VOLUME_NOTIFICATION, // deprecated since API 2?
-                    Settings.System.VOLUME_RING, // deprecated since API 2?
-                    Settings.System.VOLUME_SYSTEM, // deprecated since API 2?
-                    Settings.System.VOLUME_VOICE, // deprecated since API 2?
-                    Settings.System.WHEN_TO_MAKE_WIFI_CALLS, // bug?
-                    Settings.System.WINDOW_ORIENTATION_LISTENER_LOG, // used for debugging only
-                    Settings.System.SCREEN_BRIGHTNESS_FLOAT,
-                    Settings.System.SCREEN_BRIGHTNESS_FOR_ALS,
-                    Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE,
-                    Settings.System.WEAR_TTS_PREWARM_ENABLED,
-                    Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ,
-                    Settings.System.MULTI_AUDIO_FOCUS_ENABLED // form-factor/OEM specific
-                    );
-
     private static final Set<String> BACKUP_DENY_LIST_GLOBAL_SETTINGS =
             newHashSet(
                     Settings.Global.ACTIVITY_MANAGER_CONSTANTS,
@@ -737,6 +686,7 @@
                  Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS,
                  Settings.Secure.CONTENT_CAPTURE_ENABLED,
                  Settings.Secure.DEFAULT_INPUT_METHOD,
+                 Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD,
                  Settings.Secure.DEVICE_PAIRED,
                  Settings.Secure.DIALER_DEFAULT_APPLICATION,
                  Settings.Secure.DISABLED_PRINT_SERVICES,
@@ -862,7 +812,7 @@
         checkSettingsBackedUpOrDenied(
                 getCandidateSettings(Settings.System.class),
                 newHashSet(SystemSettings.SETTINGS_TO_BACKUP),
-                BACKUP_DENY_LIST_SYSTEM_SETTINGS);
+                getBackUpDenyListSystemSettings());
     }
 
     @Test
@@ -937,6 +887,69 @@
         checkSettingsBackedUpOrDenied(allSettings, keys, BACKUP_DENY_LIST_SECURE_SETTINGS);
     }
 
+    /**
+     * The following denylists contain settings that should *not* be backed up and restored to
+     * another device.  As a general rule, anything that is not user configurable should be
+     * denied (and conversely, things that *are* user configurable *should* be backed up)
+     */
+    private static Set<String> getBackUpDenyListSystemSettings() {
+        Set<String> settings =
+                newHashSet(
+                        Settings.System.ADVANCED_SETTINGS, // candidate for backup?
+                        Settings.System.ALARM_ALERT_CACHE, // internal cache
+                        Settings.System.APPEND_FOR_LAST_AUDIBLE, // suffix deprecated since API 2
+                        Settings.System.EGG_MODE, // I am the lolrus
+                        Settings.System.END_BUTTON_BEHAVIOR, // bug?
+                        Settings.System
+                                .HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY,
+                        // candidate for backup?
+                        Settings.System.LOCKSCREEN_DISABLED, // ?
+                        Settings.System.MEDIA_BUTTON_RECEIVER, // candidate for backup?
+                        Settings.System.MUTE_STREAMS_AFFECTED, //  candidate for backup?
+                        Settings.System.NOTIFICATION_SOUND_CACHE, // internal cache
+                        Settings.System.POINTER_LOCATION, // backup candidate?
+                        Settings.System.DEBUG_ENABLE_ENHANCED_CALL_BLOCKING,
+                        // used for testing only
+                        Settings.System.RINGTONE_CACHE, // internal cache
+                        Settings.System.SCREEN_BRIGHTNESS, // removed in P
+                        Settings.System.SETUP_WIZARD_HAS_RUN, // Only used by SuW
+                        Settings.System.SHOW_GTALK_SERVICE_STATUS, // candidate for backup?
+                        Settings.System.SHOW_TOUCHES,
+                        Settings.System.SHOW_KEY_PRESSES,
+                        Settings.System.SHOW_ROTARY_INPUT,
+                        Settings.System.SIP_ADDRESS_ONLY, // value, not a setting
+                        Settings.System.SIP_ALWAYS, // value, not a setting
+                        Settings.System.SYSTEM_LOCALES, // bug?
+                        Settings.System.USER_ROTATION, // backup candidate?
+                        Settings.System.VIBRATE_IN_SILENT, // deprecated?
+                        Settings.System.VOLUME_ACCESSIBILITY,
+                        // used internally, changing value will
+                        // not change volume
+                        Settings.System.VOLUME_ALARM, // deprecated since API 2?
+                        Settings.System.VOLUME_ASSISTANT, // candidate for backup?
+                        Settings.System.VOLUME_BLUETOOTH_SCO, // deprecated since API 2?
+                        Settings.System.VOLUME_MASTER, // candidate for backup?
+                        Settings.System.VOLUME_MUSIC, // deprecated since API 2?
+                        Settings.System.VOLUME_NOTIFICATION, // deprecated since API 2?
+                        Settings.System.VOLUME_RING, // deprecated since API 2?
+                        Settings.System.VOLUME_SYSTEM, // deprecated since API 2?
+                        Settings.System.VOLUME_VOICE, // deprecated since API 2?
+                        Settings.System.WHEN_TO_MAKE_WIFI_CALLS, // bug?
+                        Settings.System.WINDOW_ORIENTATION_LISTENER_LOG, // used for debugging only
+                        Settings.System.SCREEN_BRIGHTNESS_FLOAT,
+                        Settings.System.SCREEN_BRIGHTNESS_FOR_ALS,
+                        Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE,
+                        Settings.System.WEAR_TTS_PREWARM_ENABLED,
+                        Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ,
+                        Settings.System.MULTI_AUDIO_FOCUS_ENABLED // form-factor/OEM specific
+                );
+        if (!Flags.backUpSmoothDisplayAndForcePeakRefreshRate()) {
+            settings.add(Settings.System.MIN_REFRESH_RATE);
+            settings.add(Settings.System.PEAK_REFRESH_RATE);
+        }
+        return settings;
+    }
+
     private static void checkSettingsBackedUpOrDenied(
             Set<String> settings, Set<String> settingsToBackup, Set<String> denylist) {
         Set<String> settingsNotBackedUp = difference(settings, settingsToBackup);
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/aconfig/predictive_back.aconfig b/packages/SystemUI/aconfig/predictive_back.aconfig
new file mode 100644
index 0000000..1ad1666
--- /dev/null
+++ b/packages/SystemUI/aconfig/predictive_back.aconfig
@@ -0,0 +1,22 @@
+package: "com.android.systemui"
+
+flag {
+    name: "predictive_back_sysui"
+    namespace: "systemui"
+    description: "Predictive Back Dispatching for SysUI"
+    bug: "309545085"
+}
+
+flag {
+    name: "predictive_back_animate_shade"
+    namespace: "systemui"
+    description: "Enable Shade Animations"
+    bug: "309545085"
+}
+
+flag {
+    name: "predictive_back_animate_bouncer"
+    namespace: "systemui"
+    description: "Enable Predictive Back Animation in Bouncer"
+    bug: "309545085"
+}
\ No newline at end of file
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index aa0903c..f0093ab 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -308,6 +308,13 @@
 }
 
 flag {
+   name: "run_fingerprint_detect_on_dismissible_keyguard"
+   namespace: "systemui"
+   description: "Run fingerprint detect instead of authenticate if the keyguard is dismissible."
+   bug: "311145851"
+}
+
+flag {
    name: "bluetooth_qs_tile_dialog_auto_on_toggle"
    namespace: "systemui"
    description: "Displays the auto on toggle in the bluetooth QS tile dialog"
diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/shaderutil/ShaderUtilLibrary.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/shaderutil/ShaderUtilLibrary.kt
index 23fcb69..867bbb7d 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/shaderutil/ShaderUtilLibrary.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/shaderutil/ShaderUtilLibrary.kt
@@ -65,12 +65,33 @@
             return dest;
         }
 
-        // Return range [-1, 1].
+        // Integer mod. GLSL es 1.0 doesn't have integer mod :(
+        int imod(int a, int b) {
+            return a - (b * (a / b));
+        }
+
+        ivec3 imod(ivec3 a, int b) {
+            return ivec3(imod(a.x, b), imod(a.y, b), imod(a.z, b));
+        }
+
+        // Integer based hash function with the return range of [-1, 1].
         vec3 hash(vec3 p) {
-            p = fract(p * vec3(.3456, .1234, .9876));
-            p += dot(p, p.yxz + 43.21);
-            p = (p.xxy + p.yxx) * p.zyx;
-            return (fract(sin(p) * 4567.1234567) - .5) * 2.;
+            ivec3 v = ivec3(p);
+            v = v * 1671731 + 10139267;
+
+            v.x += v.y * v.z;
+            v.y += v.z * v.x;
+            v.z += v.x * v.y;
+
+            ivec3 v2 = v / 65536; // v >> 16
+            v = imod((10 - imod((v + v2), 10)), 10); // v ^ v2
+
+            v.x += v.y * v.z;
+            v.y += v.z * v.x;
+            v.z += v.x * v.y;
+
+            // Use sin and cos to map the range to [-1, 1].
+            return vec3(sin(float(v.x)), cos(float(v.y)), sin(float(v.z)));
         }
 
         // Skew factors (non-uniform).
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/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/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index 2b41178..3a26ebf 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -66,6 +66,7 @@
         "kotlinx_coroutines",
         "dagger2",
         "jsr330",
+        "com_android_systemui_shared_flags_lib",
     ],
     resource_dirs: [
         "res",
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 131eb6b..c08b083 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -20,10 +20,11 @@
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
 
+import static com.android.systemui.shared.Flags.shadeAllowBackGesture;
+
 import android.annotation.IntDef;
 import android.content.Context;
 import android.content.res.Resources;
-import android.os.SystemProperties;
 import android.view.ViewConfiguration;
 import android.view.WindowManagerPolicyConstants;
 
@@ -132,8 +133,7 @@
             SYSUI_STATE_WAKEFULNESS_TRANSITION | SYSUI_STATE_AWAKE;
 
     // Whether the back gesture is allowed (or ignored) by the Shade
-    public static final boolean ALLOW_BACK_GESTURE_IN_SHADE = SystemProperties.getBoolean(
-            "persist.wm.debug.shade_allow_back_gesture", false);
+    public static final boolean ALLOW_BACK_GESTURE_IN_SHADE = shadeAllowBackGesture();
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({SYSUI_STATE_SCREEN_PINNING,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index f3cd01f..e6b08c8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -113,6 +113,7 @@
 import com.android.settingslib.fuelgauge.BatteryStatus;
 import com.android.systemui.CoreStartable;
 import com.android.systemui.Dumpable;
+import com.android.systemui.Flags;
 import com.android.systemui.biometrics.AuthController;
 import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider;
 import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -217,7 +218,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;
@@ -372,6 +374,7 @@
     private List<SubscriptionInfo> mSubscriptionInfo;
     @VisibleForTesting
     protected int mFingerprintRunningState = BIOMETRIC_STATE_STOPPED;
+    private boolean mFingerprintDetectRunning;
     private boolean mIsDreaming;
     private boolean mLogoutEnabled;
     private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -985,6 +988,7 @@
         final boolean wasCancellingRestarting = mFingerprintRunningState
                 == BIOMETRIC_STATE_CANCELLING_RESTARTING;
         mFingerprintRunningState = BIOMETRIC_STATE_STOPPED;
+        mFingerprintDetectRunning = false;
         if (wasCancellingRestarting) {
             KeyguardUpdateMonitor.this.updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
         } else {
@@ -1093,6 +1097,9 @@
         boolean wasRunning = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING;
         boolean isRunning = fingerprintRunningState == BIOMETRIC_STATE_RUNNING;
         mFingerprintRunningState = fingerprintRunningState;
+        if (mFingerprintRunningState == BIOMETRIC_STATE_STOPPED) {
+            mFingerprintDetectRunning = false;
+        }
         mLogger.logFingerprintRunningState(mFingerprintRunningState);
         // Clients of KeyguardUpdateMonitor don't care about the internal state about the
         // asynchronousness of the cancel cycle. So only notify them if the actually running state
@@ -1803,6 +1810,7 @@
                 public void onFingerprintDetected(int sensorId, int userId,
                         boolean isStrongBiometric) {
                     handleBiometricDetected(userId, FINGERPRINT, isStrongBiometric);
+                    setFingerprintRunningState(BIOMETRIC_STATE_STOPPED);
                 }
             };
 
@@ -2064,6 +2072,7 @@
     @VisibleForTesting
     void resetBiometricListeningState() {
         mFingerprintRunningState = BIOMETRIC_STATE_STOPPED;
+        mFingerprintDetectRunning = false;
     }
 
     @VisibleForTesting
@@ -2502,8 +2511,10 @@
             return;
         }
         final boolean shouldListenForFingerprint = shouldListenForFingerprint(isUdfpsSupported());
-        final boolean runningOrRestarting = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING
+        final boolean running = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING;
+        final boolean runningOrRestarting = running
                 || mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING;
+        final boolean runDetect = shouldRunFingerprintDetect();
         if (runningOrRestarting && !shouldListenForFingerprint) {
             if (action == BIOMETRIC_ACTION_START) {
                 mLogger.v("Ignoring stopListeningForFingerprint()");
@@ -2515,10 +2526,24 @@
                 mLogger.v("Ignoring startListeningForFingerprint()");
                 return;
             }
-            startListeningForFingerprint();
+            startListeningForFingerprint(runDetect);
+        } else if (running && runDetect && !mFingerprintDetectRunning) {
+            if (action == BIOMETRIC_ACTION_STOP) {
+                mLogger.v("Ignoring startListeningForFingerprint(detect)");
+                return;
+            }
+            // stop running authentication and start running fingerprint detection
+            stopListeningForFingerprint();
+            startListeningForFingerprint(true);
         }
     }
 
+    private boolean shouldRunFingerprintDetect() {
+        return !isUnlockingWithFingerprintAllowed()
+                || (Flags.runFingerprintDetectOnDismissibleKeyguard()
+                && getUserCanSkipBouncer(mSelectedUserInteractor.getSelectedUserId()));
+    }
+
     /**
      * If a user is encrypted or not.
      * This is NOT related to the lock screen being visible or not.
@@ -2774,7 +2799,6 @@
                         && biometricEnabledForUser
                         && !isUserInLockdown(user);
         final boolean strongerAuthRequired = !isUnlockingWithFingerprintAllowed();
-        final boolean isSideFps = isSfpsSupported() && isSfpsEnrolled();
         final boolean shouldListenBouncerState =
                 !strongerAuthRequired || !mPrimaryBouncerIsOrWillBeShowing;
 
@@ -2837,7 +2861,7 @@
         }
     }
 
-    private void startListeningForFingerprint() {
+    private void startListeningForFingerprint(boolean runDetect) {
         final int userId = mSelectedUserInteractor.getSelectedUserId();
         final boolean unlockPossible = isUnlockWithFingerprintPossible(userId);
         if (mFingerprintCancelSignal != null) {
@@ -2867,18 +2891,20 @@
                         mFingerprintInteractiveToAuthProvider.getVendorExtension(userId));
             }
 
-            if (!isUnlockingWithFingerprintAllowed()) {
+            if (runDetect) {
                 mLogger.v("startListeningForFingerprint - detect");
                 mFpm.detectFingerprint(
                         mFingerprintCancelSignal,
                         mFingerprintDetectionCallback,
                         fingerprintAuthenticateOptions);
+                mFingerprintDetectRunning = true;
             } else {
                 mLogger.v("startListeningForFingerprint");
                 mFpm.authenticate(null /* crypto */, mFingerprintCancelSignal,
                         mFingerprintAuthenticationCallback,
                         null /* handler */,
                         fingerprintAuthenticateOptions);
+                mFingerprintDetectRunning = false;
             }
             setFingerprintRunningState(BIOMETRIC_STATE_RUNNING);
         }
@@ -3874,6 +3900,7 @@
                 mSelectedUserInteractor.getSelectedUserId()));
         pw.println("  getUserUnlockedWithBiometric()="
                 + getUserUnlockedWithBiometric(mSelectedUserInteractor.getSelectedUserId()));
+        pw.println("  mFingerprintDetectRunning=" + mFingerprintDetectRunning);
         pw.println("  SIM States:");
         for (SimData data : mSimDatas.values()) {
             pw.println("    " + data.toString());
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/back/domain/interactor/BackActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt
index 066cba23..6076f32 100644
--- a/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt
@@ -22,10 +22,9 @@
 import android.window.OnBackInvokedDispatcher
 import android.window.WindowOnBackInvokedDispatcher
 import com.android.systemui.CoreStartable
+import com.android.systemui.Flags.predictiveBackAnimateShade
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
 import com.android.systemui.shade.QuickSettingsController
@@ -48,14 +47,13 @@
     private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
     private val shadeController: ShadeController,
     private val notificationShadeWindowController: NotificationShadeWindowController,
-    private val windowRootViewVisibilityInteractor: WindowRootViewVisibilityInteractor,
-    featureFlags: FeatureFlags,
+    private val windowRootViewVisibilityInteractor: WindowRootViewVisibilityInteractor
 ) : CoreStartable {
 
     private var isCallbackRegistered = false
 
     private val callback =
-        if (featureFlags.isEnabled(Flags.WM_SHADE_ANIMATE_BACK_GESTURE)) {
+        if (predictiveBackAnimateShade()) {
             /**
              * New callback that handles back gesture invoked, cancel, progress and provides
              * feedback via Shade animation.
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index 6345312..45967c6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -32,13 +32,13 @@
 import com.android.settingslib.Utils
 import com.android.systemui.CoreStartable
 import com.android.systemui.Flags.lightRevealMigration
-import com.android.systemui.res.R
 import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.log.core.LogLevel
 import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.res.R
 import com.android.systemui.statusbar.CircleReveal
 import com.android.systemui.statusbar.LiftReveal
 import com.android.systemui.statusbar.LightRevealEffect
@@ -50,6 +50,7 @@
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.util.ViewController
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import java.io.PrintWriter
 import javax.inject.Inject
 import javax.inject.Provider
@@ -62,6 +63,7 @@
  *
  * The ripple uses the accent color of the current theme.
  */
+@ExperimentalCoroutinesApi
 @SysUISingleton
 class AuthRippleController @Inject constructor(
     private val sysuiContext: Context,
@@ -75,7 +77,6 @@
     private val udfpsControllerProvider: Provider<UdfpsController>,
     private val statusBarStateController: StatusBarStateController,
     private val displayMetrics: DisplayMetrics,
-    private val featureFlags: FeatureFlags,
     private val logger: KeyguardLogger,
     private val biometricUnlockController: BiometricUnlockController,
     private val lightRevealScrim: LightRevealScrim,
@@ -313,6 +314,18 @@
                 mView.fadeDwellRipple()
             }
         }
+
+        override fun onBiometricDetected(
+                userId: Int,
+                biometricSourceType: BiometricSourceType,
+                isStrongBiometric: Boolean
+        ) {
+            // TODO (b/309804148): add support detect auth ripple for deviceEntryUdfpsRefactor
+            if (!DeviceEntryUdfpsRefactor.isEnabled &&
+                    keyguardUpdateMonitor.getUserCanSkipBouncer(userId)) {
+                showUnlockRipple(biometricSourceType)
+            }
+        }
     }
 
     private val configurationChangedListener =
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/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index bb0c273..38c7c6a 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -444,29 +444,10 @@
     // TODO(b/254512728): Tracking Bug
     @JvmField val NEW_BACK_AFFORDANCE = releasedFlag("new_back_affordance")
 
-    // TODO(b/255854141): Tracking Bug
-    @JvmField
-    val WM_ENABLE_PREDICTIVE_BACK_SYSUI =
-        unreleasedFlag("persist.wm.debug.predictive_back_sysui_enable", teamfood = true)
 
     // TODO(b/270987164): Tracking Bug
     @JvmField val TRACKPAD_GESTURE_FEATURES = releasedFlag("trackpad_gesture_features")
 
-    // TODO(b/263826204): Tracking Bug
-    @JvmField
-    val WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM =
-        unreleasedFlag("persist.wm.debug.predictive_back_bouncer_anim", teamfood = true)
-
-    // TODO(b/238475428): Tracking Bug
-    @JvmField
-    val WM_SHADE_ALLOW_BACK_GESTURE =
-        sysPropBooleanFlag("persist.wm.debug.shade_allow_back_gesture", default = false)
-
-    // TODO(b/238475428): Tracking Bug
-    @JvmField
-    val WM_SHADE_ANIMATE_BACK_GESTURE =
-        unreleasedFlag("persist.wm.debug.shade_animate_back_gesture", teamfood = false)
-
     // TODO(b/265639042): Tracking Bug
     @JvmField
     val WM_ENABLE_PREDICTIVE_BACK_QS_DIALOG_ANIM =
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/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 3e50dd3..ac0bd29 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -49,6 +49,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
 import java.util.Objects;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
@@ -231,32 +232,39 @@
         if (!collapsedView && mQsTileRevealController != null) {
             mQsTileRevealController.updateRevealedTiles(tiles);
         }
-        boolean shouldChange = false;
+        boolean shouldChangeAll = false;
+        // If the new tiles are a prefix of the old tiles, we delete the extra tiles (from the old).
+        // If not (even if they share a prefix) we remove all and add all the new ones.
         if (tiles.size() <= mRecords.size()) {
             int i = 0;
+            // Iterate through the requested tiles and check if they are the same as the existing
+            // tiles.
             for (QSTile tile : tiles) {
                 if (tile != mRecords.get(i).tile) {
-                    shouldChange = true;
+                    shouldChangeAll = true;
                     break;
                 }
                 i++;
             }
 
-            // If the first tiles are the same as the new ones, remove any extras.
-            if (!shouldChange) {
-                while (i < mRecords.size()) {
-                    QSPanelControllerBase.TileRecord record = mRecords.get(i);
+            // If the first tiles are the same as the new ones, we reuse them and remove any extra
+            // tiles.
+            if (!shouldChangeAll && i < mRecords.size()) {
+                List<TileRecord> extraRecords = mRecords.subList(i, mRecords.size());
+                for (QSPanelControllerBase.TileRecord record : extraRecords) {
                     mView.removeTile(record);
                     record.tile.removeCallback(record.callback);
-                    i++;
                 }
+                extraRecords.clear();
                 mCachedSpecs = getTilesSpecs();
             }
         } else {
-            shouldChange = true;
+            shouldChangeAll = true;
         }
 
-        if (shouldChange) {
+        // If we detected that the existing tiles are different than the requested tiles, clear them
+        // and add the new tiles.
+        if (shouldChangeAll) {
             for (QSPanelControllerBase.TileRecord record : mRecords) {
                 mView.removeTile(record);
                 record.tile.removeCallback(record.callback);
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/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index fb6bc38..026201e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -26,6 +26,7 @@
 import static com.android.keyguard.KeyguardClockSwitch.SMALL;
 import static com.android.systemui.Flags.keyguardBottomAreaRefactor;
 import static com.android.systemui.Flags.migrateClocksToBlueprint;
+import static com.android.systemui.Flags.predictiveBackAnimateShade;
 import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK;
 import static com.android.systemui.classifier.Classifier.GENERIC;
 import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
@@ -891,7 +892,7 @@
         mShadeHeaderController = shadeHeaderController;
         mLayoutInflater = layoutInflater;
         mFeatureFlags = featureFlags;
-        mAnimateBack = mFeatureFlags.isEnabled(Flags.WM_SHADE_ANIMATE_BACK_GESTURE);
+        mAnimateBack = predictiveBackAnimateShade();
         mTrackpadGestureFeaturesEnabled = mFeatureFlags.isEnabled(Flags.TRACKPAD_GESTURE_FEATURES);
         mFalsingCollector = falsingCollector;
         mWakeUpCoordinator = coordinator;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 57d49b2..6e3aabf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -31,6 +31,7 @@
 import static com.android.systemui.charging.WirelessChargingAnimation.UNKNOWN_BATTERY_LEVEL;
 import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF;
 import static com.android.systemui.statusbar.StatusBarState.SHADE;
+import static com.android.systemui.Flags.predictiveBackSysui;
 
 import android.annotation.Nullable;
 import android.app.ActivityOptions;
@@ -836,7 +837,7 @@
         mLightRevealScrim = lightRevealScrim;
 
         // Based on teamfood flag, turn predictive back dispatch on at runtime.
-        if (mFeatureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_SYSUI)) {
+        if (predictiveBackSysui()) {
             mContext.getApplicationInfo().setEnableOnBackInvokedCallback(true);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 4999123..88347ab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -18,6 +18,7 @@
 
 import static android.view.WindowInsets.Type.navigationBars;
 
+import static com.android.systemui.Flags.predictiveBackAnimateBouncer;
 import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN;
 import static com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
@@ -400,8 +401,7 @@
         mFoldAodAnimationController = sysUIUnfoldComponent
                 .map(SysUIUnfoldComponent::getFoldAodAnimationController).orElse(null);
         mAlternateBouncerInteractor = alternateBouncerInteractor;
-        mIsBackAnimationEnabled =
-                featureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM);
+        mIsBackAnimationEnabled = predictiveBackAnimateBouncer();
         mUdfpsOverlayInteractor = udfpsOverlayInteractor;
         mActivityStarter = activityStarter;
         mKeyguardTransitionInteractor = keyguardTransitionInteractor;
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..9642895 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;
@@ -119,6 +120,7 @@
 import com.android.keyguard.KeyguardUpdateMonitor.BiometricAuthenticated;
 import com.android.keyguard.logging.KeyguardUpdateMonitorLogger;
 import com.android.settingslib.fuelgauge.BatteryStatus;
+import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.biometrics.AuthController;
 import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider;
@@ -922,8 +924,7 @@
     @Test
     public void trustAgentHasTrust() {
         // WHEN user has trust
-        mKeyguardUpdateMonitor.onTrustChanged(true, true,
-                mSelectedUserInteractor.getSelectedUserId(), 0, null);
+        givenSelectedUserCanSkipBouncerFromTrustedState();
 
         // THEN user is considered as "having trust" and bouncer can be skipped
         Assert.assertTrue(mKeyguardUpdateMonitor.getUserHasTrust(
@@ -947,8 +948,7 @@
     @Test
     public void trustAgentHasTrust_fingerprintLockout() {
         // GIVEN user has trust
-        mKeyguardUpdateMonitor.onTrustChanged(true, true,
-                mSelectedUserInteractor.getSelectedUserId(), 0, null);
+        givenSelectedUserCanSkipBouncerFromTrustedState();
         Assert.assertTrue(mKeyguardUpdateMonitor.getUserHasTrust(
                 mSelectedUserInteractor.getSelectedUserId()));
 
@@ -1973,6 +1973,61 @@
     }
 
     @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 runFpDetectFlagDisabled_sideFps_keyguardDismissible_fingerprintAuthenticateRuns() {
+        mSetFlagsRule.disableFlags(Flags.FLAG_RUN_FINGERPRINT_DETECT_ON_DISMISSIBLE_KEYGUARD);
+
+        // Clear invocations, since previous setup (e.g. registering BiometricManager callbacks)
+        // will trigger updateBiometricListeningState();
+        clearInvocations(mFingerprintManager);
+        mKeyguardUpdateMonitor.resetBiometricListeningState();
+
+        // GIVEN the user can skip the bouncer
+        givenSelectedUserCanSkipBouncerFromTrustedState();
+        when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
+        mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
+        mTestableLooper.processAllMessages();
+
+        // WHEN verify authenticate runs
+        verifyFingerprintAuthenticateCall();
+    }
+
+    @Test
+    public void sideFps_keyguardDismissible_fingerprintDetectRuns() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_RUN_FINGERPRINT_DETECT_ON_DISMISSIBLE_KEYGUARD);
+        // Clear invocations, since previous setup (e.g. registering BiometricManager callbacks)
+        // will trigger updateBiometricListeningState();
+        clearInvocations(mFingerprintManager);
+        mKeyguardUpdateMonitor.resetBiometricListeningState();
+
+        // GIVEN the user can skip the bouncer
+        givenSelectedUserCanSkipBouncerFromTrustedState();
+        when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
+        mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
+        mTestableLooper.processAllMessages();
+
+        // WHEN verify detect runs
+        verifyFingerprintDetectCall();
+    }
+
+    @Test
     public void testFingerprintSensorProperties() throws RemoteException {
         mFingerprintAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered(
                 new ArrayList<>());
@@ -2077,6 +2132,11 @@
         verify(callback).onBiometricEnrollmentStateChanged(BiometricSourceType.FACE);
     }
 
+    private void givenSelectedUserCanSkipBouncerFromTrustedState() {
+        mKeyguardUpdateMonitor.onTrustChanged(true, true,
+                mSelectedUserInteractor.getSelectedUserId(), 0, null);
+    }
+
     private void verifyFingerprintAuthenticateNeverCalled() {
         verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), any());
         verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
index 8693d5c..7c626a1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
@@ -16,6 +16,9 @@
 
 package com.android.systemui.back.domain.interactor
 
+import android.platform.test.annotations.RequiresFlagsDisabled
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
 import android.view.ViewRootImpl
 import android.window.BackEvent
 import android.window.BackEvent.EDGE_LEFT
@@ -26,9 +29,8 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.statusbar.IStatusBarService
+import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
@@ -72,7 +74,6 @@
 @OptIn(ExperimentalCoroutinesApi::class)
 class BackActionInteractorTest : SysuiTestCase() {
     private val testScope = TestScope()
-    private val featureFlags = FakeFeatureFlags()
     private val executor = FakeExecutor(FakeSystemClock())
 
     @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
@@ -107,17 +108,17 @@
                 statusBarKeyguardViewManager,
                 shadeController,
                 notificationShadeWindowController,
-                windowRootViewVisibilityInteractor,
-                featureFlags,
+                windowRootViewVisibilityInteractor
             )
             .apply { this.setup(qsController, shadeViewController) }
     }
 
     private val powerInteractor = PowerInteractorFactory.create().powerInteractor
 
+    @get:Rule val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
     @Before
     fun setUp() {
-        featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, false)
         whenever(notificationShadeWindowController.windowRootView).thenReturn(windowRootView)
         whenever(windowRootView.viewRootImpl).thenReturn(viewRootImpl)
         whenever(viewRootImpl.onBackInvokedDispatcher).thenReturn(onBackInvokedDispatcher)
@@ -229,9 +230,9 @@
     }
 
     @Test
+    @RequiresFlagsDisabled(Flags.FLAG_PREDICTIVE_BACK_ANIMATE_SHADE)
     fun animationFlagOff_onBackInvoked_keyguardNotified() {
         backActionInteractor.start()
-        featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, false)
         windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
         powerInteractor.setAwakeForTest()
         val callback = getBackInvokedCallback()
@@ -243,8 +244,8 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_PREDICTIVE_BACK_ANIMATE_SHADE)
     fun animationFlagOn_onBackInvoked_keyguardNotified() {
-        featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true)
         backActionInteractor.start()
         windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
         powerInteractor.setAwakeForTest()
@@ -257,8 +258,8 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_PREDICTIVE_BACK_ANIMATE_SHADE)
     fun animationFlagOn_callbackIsAnimationCallback() {
-        featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true)
         backActionInteractor.start()
         windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
         powerInteractor.setAwakeForTest()
@@ -269,8 +270,8 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_PREDICTIVE_BACK_ANIMATE_SHADE)
     fun onBackProgressed_shadeCannotBeCollapsed_shadeViewControllerNotNotified() {
-        featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true)
         backActionInteractor.start()
         windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
         powerInteractor.setAwakeForTest()
@@ -284,8 +285,8 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_PREDICTIVE_BACK_ANIMATE_SHADE)
     fun onBackProgressed_shadeCanBeCollapsed_shadeViewControllerNotified() {
-        featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true)
         backActionInteractor.start()
         windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
         powerInteractor.setAwakeForTest()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
index 1f7dd6d..c143bc0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
@@ -123,7 +123,6 @@
             udfpsControllerProvider,
             statusBarStateController,
             displayMetrics,
-            featureFlags,
             KeyguardLogger(logcatLogBuffer(AuthRippleController.TAG)),
             biometricUnlockController,
             lightRevealScrim,
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/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
index 018fa9e..a92111e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -394,6 +394,7 @@
     public void setTiles_differentTiles_extraTileRemoved() {
         when(mQSHost.getTiles()).thenReturn(List.of(mQSTile, mOtherTile));
         mController.setTiles();
+        assertEquals(2, mController.mRecords.size());
 
         clearInvocations(mQSPanel);
 
@@ -402,6 +403,7 @@
 
         verify(mQSPanel, times(1)).removeTile(any());
         verify(mQSPanel, never()).addTile(any());
+        assertEquals(1, mController.mRecords.size());
     }
 
     @Test
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 2220756..b0792d8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -382,7 +382,6 @@
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
-        mFeatureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, false);
         mFeatureFlags.set(Flags.TRACKPAD_GESTURE_FEATURES, false);
         mFeatureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false);
         mFeatureFlags.set(Flags.QS_USER_DETAIL_SHORTCUT, false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index e339636..316f2b9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -334,15 +334,8 @@
     public void setup() throws Exception {
         MockitoAnnotations.initMocks(this);
 
-        // CentralSurfacesImpl's runtime flag check fails if the flag is absent.
-        // This value is unused, because test manifest is opted in.
-        mFeatureFlags.set(Flags.WM_ENABLE_PREDICTIVE_BACK_SYSUI, false);
         // Set default value to avoid IllegalStateException.
         mFeatureFlags.set(Flags.SHORTCUT_LIST_SEARCH_LAYOUT, false);
-        // For the Shade to respond to Back gesture, we must enable the event routing
-        mFeatureFlags.set(Flags.WM_SHADE_ALLOW_BACK_GESTURE, true);
-        // For the Shade to animate during the Back gesture, we must enable the animation flag.
-        mFeatureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true);
         mSetFlagsRule.enableFlags(FLAG_LIGHT_REVEAL_MIGRATION);
         // Turn AOD on and toggle feature flag for jank fixes
         mFeatureFlags.set(Flags.ZJ_285570694_LOCKSCREEN_TRANSITION_FROM_AOD, true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 225ddb6..8dde935 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -37,6 +37,9 @@
 
 import static kotlinx.coroutines.test.TestCoroutineDispatchersKt.StandardTestDispatcher;
 
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.service.trust.TrustAgentService;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -98,6 +101,7 @@
 import com.google.common.truth.Truth;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -165,6 +169,8 @@
     @Captor
     private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardUpdateMonitorCallback;
 
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
 
     @Before
     public void setUp() {
@@ -175,7 +181,6 @@
         when(mBouncerView.getDelegate()).thenReturn(mBouncerViewDelegate);
         when(mBouncerViewDelegate.getBackCallback()).thenReturn(mBouncerViewDelegateBackCallback);
         mFeatureFlags = new FakeFeatureFlags();
-        mFeatureFlags.set(Flags.WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM, true);
         mFeatureFlags.set(Flags.REFACTOR_KEYGUARD_DISMISS_INTENT, false);
         mFeatureFlags.set(Flags.KEYGUARD_WM_STATE_REFACTOR, false);
         mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
@@ -584,6 +589,7 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_PREDICTIVE_BACK_ANIMATE_BOUNCER)
     public void testPredictiveBackCallback_registration() {
         /* verify that a predictive back callback is registered when the bouncer becomes visible */
         mBouncerExpansionCallback.onVisibilityChanged(true);
@@ -598,6 +604,7 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_PREDICTIVE_BACK_ANIMATE_BOUNCER)
     public void testPredictiveBackCallback_invocationHidesBouncer() {
         mBouncerExpansionCallback.onVisibilityChanged(true);
         /* capture the predictive back callback during registration */
@@ -615,6 +622,7 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_PREDICTIVE_BACK_ANIMATE_BOUNCER)
     public void testPredictiveBackCallback_noBackAnimationForFullScreenBouncer() {
         when(mKeyguardSecurityModel.getSecurityMode(anyInt()))
                 .thenReturn(KeyguardSecurityModel.SecurityMode.SimPin);
@@ -634,6 +642,7 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_PREDICTIVE_BACK_ANIMATE_BOUNCER)
     public void testPredictiveBackCallback_forwardsBackDispatches() {
         mBouncerExpansionCallback.onVisibilityChanged(true);
         /* capture the predictive back callback during registration */
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/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/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
index 6b0fdb5..cf414d1 100644
--- a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
@@ -240,10 +240,20 @@
         mEventInternal = Optional.of(new PresentationStatsEventInternal());
     }
 
+    /**
+     * Set request_id
+     */
     public void maybeSetRequestId(int requestId) {
         mEventInternal.ifPresent(event -> event.mRequestId = requestId);
     }
 
+    /**
+     * Set is_credential_request
+     */
+    public void maybeSetIsCredentialRequest(boolean isCredentialRequest) {
+        mEventInternal.ifPresent(event -> event.mIsCredentialRequest = isCredentialRequest);
+    }
+
     public void maybeSetNoPresentationEventReason(@NotShownReason int reason) {
         mEventInternal.ifPresent(event -> {
             if (event.mCountShown == 0) {
@@ -567,7 +577,8 @@
                     + " mSelectedDatasetPickedReason=" + event.mSelectedDatasetPickedReason
                     + " mDetectionPreference=" + event.mDetectionPreference
                     + " mFieldClassificationRequestId=" + event.mFieldClassificationRequestId
-                    + " mAppPackageUid=" + mCallingAppUid);
+                    + " mAppPackageUid=" + mCallingAppUid
+                    + " mIsCredentialRequest=" + event.mIsCredentialRequest);
         }
 
         // TODO(b/234185326): Distinguish empty responses from other no presentation reasons.
@@ -606,7 +617,8 @@
                 event.mSelectedDatasetPickedReason,
                 event.mDetectionPreference,
                 event.mFieldClassificationRequestId,
-                mCallingAppUid);
+                mCallingAppUid,
+                event.mIsCredentialRequest);
         mEventInternal = Optional.empty();
     }
 
@@ -640,6 +652,7 @@
         @DatasetPickedReason int mSelectedDatasetPickedReason = PICK_REASON_UNKNOWN;
         @DetectionPreference int mDetectionPreference = DETECTION_PREFER_UNKNOWN;
         int mFieldClassificationRequestId = -1;
+        boolean mIsCredentialRequest = false;
 
         PresentationStatsEventInternal() {}
     }
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index a49f9db..007be05 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1290,7 +1290,9 @@
             Slog.v(TAG, "Requesting structure for request #" + ordinal + " ,requestId=" + requestId
                     + ", flags=" + flags);
         }
+        boolean isCredmanRequested = (flags & FLAG_VIEW_REQUESTS_CREDMAN_SERVICE) != 0;
         mPresentationStatsEventLogger.maybeSetRequestId(requestId);
+        mPresentationStatsEventLogger.maybeSetIsCredentialRequest(isCredmanRequested);
         mPresentationStatsEventLogger.maybeSetFieldClassificationRequestId(
                 mFieldClassificationIdSnapshot);
         mFillRequestEventLogger.maybeSetRequestId(requestId);
@@ -4360,8 +4362,10 @@
                 }
 
                 if (viewState.getResponse() != null) {
+                    boolean isCredmanRequested = (flags & FLAG_VIEW_REQUESTS_CREDMAN_SERVICE) != 0;
                     FillResponse response = viewState.getResponse();
                     mPresentationStatsEventLogger.maybeSetRequestId(response.getRequestId());
+                    mPresentationStatsEventLogger.maybeSetIsCredentialRequest(isCredmanRequested);
                     mPresentationStatsEventLogger.maybeSetFieldClassificationRequestId(
                             mFieldClassificationIdSnapshot);
                     mPresentationStatsEventLogger.maybeSetAvailableCount(
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index b20ed7c..e7e721b 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -162,7 +162,7 @@
         "pdf_viewer",
         "pixel_audio_android",
         "pixel_bluetooth",
-        "pixel_system_sw_touch",
+        "pixel_system_sw_video",
         "pixel_watch",
         "platform_security",
         "power",
@@ -174,6 +174,7 @@
         "safety_center",
         "sensors",
         "system_performance",
+        "system_sw_touch",
         "system_sw_usb",
         "test_suites",
         "text",
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/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 7ac4dd3..a1b6f29 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -7879,7 +7879,6 @@
         DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.addAll(AudioSystem.DEVICE_OUT_ALL_A2DP_SET);
         DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.addAll(AudioSystem.DEVICE_OUT_ALL_BLE_SET);
         DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.addAll(AudioSystem.DEVICE_OUT_ALL_USB_SET);
-        DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.add(AudioSystem.DEVICE_OUT_HDMI);
     }
 
     /** only public for mocking/spying, do not call outside of AudioService */
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/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index 6e503cb..030585f 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -970,9 +970,14 @@
 
                 if (!mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled) {
                     // The flag had been turned off, we need to restore the original value
-                    Settings.System.putFloatForUser(cr,
-                            Settings.System.MIN_REFRESH_RATE, minRefreshRate, cr.getUserId());
+                    Settings.System.putFloatForUser(cr, Settings.System.MIN_REFRESH_RATE,
+                            highestRefreshRate, cr.getUserId());
                 }
+            } else if (mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled
+                    && Math.round(minRefreshRate) == Math.round(highestRefreshRate)) {
+                // The flag has been turned on, we need to upgrade the setting
+                Settings.System.putFloatForUser(cr, Settings.System.MIN_REFRESH_RATE,
+                        Float.POSITIVE_INFINITY, cr.getUserId());
             }
 
             float peakRefreshRate = Settings.System.getFloatForUser(cr,
@@ -983,9 +988,14 @@
 
                 if (!mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled) {
                     // The flag had been turned off, we need to restore the original value
-                    Settings.System.putFloatForUser(cr,
-                            Settings.System.PEAK_REFRESH_RATE, peakRefreshRate, cr.getUserId());
+                    Settings.System.putFloatForUser(cr, Settings.System.PEAK_REFRESH_RATE,
+                            highestRefreshRate, cr.getUserId());
                 }
+            } else if (mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled
+                    && Math.round(peakRefreshRate) == Math.round(highestRefreshRate)) {
+                // The flag has been turned on, we need to upgrade the setting
+                Settings.System.putFloatForUser(cr, Settings.System.PEAK_REFRESH_RATE,
+                        Float.POSITIVE_INFINITY, cr.getUserId());
             }
 
             updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 24bcb4e..cf70d61 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.inputmethod;
 
+import static android.content.Context.DEVICE_ID_DEFAULT;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
 import static android.os.IServiceManager.DUMP_FLAG_PROTO;
@@ -315,7 +316,9 @@
     // All known input methods.
     final ArrayList<InputMethodInfo> mMethodList = new ArrayList<>();
     private final ArrayMap<String, InputMethodInfo> mMethodMap = new ArrayMap<>();
+
     // Mapping from deviceId to the device-specific imeId for that device.
+    @GuardedBy("ImfLock.class")
     private final SparseArray<String> mVirtualDeviceMethodMap = new SparseArray<>();
 
     private final InputMethodSubtypeSwitchingController mSwitchingController;
@@ -339,6 +342,9 @@
     @GuardedBy("ImfLock.class")
     private int mDisplayIdToShowIme = INVALID_DISPLAY;
 
+    @GuardedBy("ImfLock.class")
+    private int mDeviceIdToShowIme = DEVICE_ID_DEFAULT;
+
     @Nullable private StatusBarManagerInternal mStatusBarManagerInternal;
     private boolean mShowOngoingImeSwitcherForPhones;
     @GuardedBy("ImfLock.class")
@@ -2464,11 +2470,7 @@
             @StartInputReason int startInputReason,
             int unverifiedTargetSdkVersion,
             @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
-        // If no method is currently selected, do nothing.
-        final String selectedMethodId = getSelectedMethodIdLocked();
-        if (selectedMethodId == null) {
-            return InputBindResult.NO_IME;
-        }
+        String selectedMethodId = getSelectedMethodIdLocked();
 
         if (!mSystemReady) {
             // If the system is not yet ready, we shouldn't be running third
@@ -2493,8 +2495,21 @@
             return InputBindResult.NOT_IME_TARGET_WINDOW;
         }
         final int csDisplayId = cs.mSelfReportedDisplayId;
+        final int oldDisplayIdToShowIme = mDisplayIdToShowIme;
         mDisplayIdToShowIme = mVisibilityStateComputer.computeImeDisplayId(winState, csDisplayId);
 
+        // Potentially override the selected input method if the new display belongs to a virtual
+        // device with a custom IME.
+        if (oldDisplayIdToShowIme != mDisplayIdToShowIme) {
+            final String deviceMethodId = computeCurrentDeviceMethodIdLocked(selectedMethodId);
+            if (deviceMethodId == null) {
+                mVisibilityStateComputer.getImePolicy().setImeHiddenByDisplayPolicy(true);
+            } else if (!Objects.equals(deviceMethodId, selectedMethodId)) {
+                setInputMethodLocked(deviceMethodId, NOT_A_SUBTYPE_ID, mDeviceIdToShowIme);
+                selectedMethodId = deviceMethodId;
+            }
+        }
+
         if (mVisibilityStateComputer.getImePolicy().isImeHiddenByDisplayPolicy()) {
             hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */, 0 /* flags */,
                     null /* resultReceiver */,
@@ -2502,6 +2517,11 @@
             return InputBindResult.NO_IME;
         }
 
+        // If no method is currently selected, do nothing.
+        if (selectedMethodId == null) {
+            return InputBindResult.NO_IME;
+        }
+
         if (mCurClient != cs) {
             prepareClientSwitchLocked(cs);
         }
@@ -2568,6 +2588,62 @@
         return mBindingController.bindCurrentMethod();
     }
 
+    /**
+     * Update the current deviceId and return the relevant imeId for this device.
+     *   1. If the device changes to virtual and its custom IME is not available, then disable IME.
+     *   2. If the device changes to virtual with valid custom IME, then return the custom IME. If
+     *      the old device was default, then store the current imeId so it can be restored.
+     *   3. If the device changes to default, restore the default device IME.
+     *   4. Otherwise keep the current imeId.
+     */
+    @GuardedBy("ImfLock.class")
+    private String computeCurrentDeviceMethodIdLocked(String currentMethodId) {
+        if (mVdmInternal == null) {
+            mVdmInternal = LocalServices.getService(VirtualDeviceManagerInternal.class);
+        }
+        if (mVdmInternal == null || !android.companion.virtual.flags.Flags.vdmCustomIme()) {
+            return currentMethodId;
+        }
+
+        final int oldDeviceId = mDeviceIdToShowIme;
+        mDeviceIdToShowIme = mVdmInternal.getDeviceIdForDisplayId(mDisplayIdToShowIme);
+        if (mDeviceIdToShowIme == oldDeviceId) {
+            return currentMethodId;
+        }
+        if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) {
+            final String defaultDeviceMethodId = mSettings.getSelectedDefaultDeviceInputMethod();
+            if (DEBUG) {
+                Slog.v(TAG, "Restoring default device input method: " + defaultDeviceMethodId);
+            }
+            return defaultDeviceMethodId;
+        }
+
+        final String deviceMethodId =
+                mVirtualDeviceMethodMap.get(mDeviceIdToShowIme, currentMethodId);
+        if (Objects.equals(deviceMethodId, currentMethodId)) {
+            return currentMethodId;
+        } else if (!mMethodMap.containsKey(deviceMethodId)) {
+            if (DEBUG) {
+                Slog.v(TAG, "Disabling IME on virtual device with id " + mDeviceIdToShowIme
+                        + " because its custom input method is not available: " + deviceMethodId);
+            }
+            return null;
+        }
+
+        if (oldDeviceId == DEVICE_ID_DEFAULT) {
+            if (DEBUG) {
+                Slog.v(TAG, "Storing default device input method " + currentMethodId);
+            }
+            mSettings.putSelectedDefaultDeviceInputMethod(currentMethodId);
+        }
+        if (DEBUG) {
+            Slog.v(TAG, "Switching current input method from " + currentMethodId
+                    + " to device-specific one " + deviceMethodId + " because the current display "
+                    + mDisplayIdToShowIme + " belongs to device with id " + mDeviceIdToShowIme);
+        }
+        return deviceMethodId;
+    }
+
     @GuardedBy("ImfLock.class")
     void invalidateAutofillSessionLocked() {
         mAutofillController.invalidateAutofillSession();
@@ -3242,6 +3318,11 @@
 
     @GuardedBy("ImfLock.class")
     void setInputMethodLocked(String id, int subtypeId) {
+        setInputMethodLocked(id, subtypeId, DEVICE_ID_DEFAULT);
+    }
+
+    @GuardedBy("ImfLock.class")
+    void setInputMethodLocked(String id, int subtypeId, int deviceId) {
         InputMethodInfo info = mMethodMap.get(id);
         if (info == null) {
             throw getExceptionForUnknownImeId(id);
@@ -3285,6 +3366,14 @@
         }
 
         // Changing to a different IME.
+        if (mDeviceIdToShowIme != DEVICE_ID_DEFAULT && deviceId == DEVICE_ID_DEFAULT) {
+            // This change should only be applicable to the default device but the current input
+            // method is a custom one specific to a virtual device. So only update the settings
+            // entry used to restore the default device input method once we want to show the IME
+            // back on the default device.
+            mSettings.putSelectedDefaultDeviceInputMethod(id);
+            return;
+        }
         IInputMethodInvoker curMethod = getCurMethodLocked();
         if (curMethod != null) {
             curMethod.removeStylusHandwritingWindow();
@@ -5308,11 +5397,21 @@
             StringBuilder builder = new StringBuilder();
             if (mSettings.buildAndPutEnabledInputMethodsStrRemovingIdLocked(
                     builder, enabledInputMethodsList, id)) {
-                // Disabled input method is currently selected, switch to another one.
-                final String selId = mSettings.getSelectedInputMethod();
-                if (id.equals(selId) && !chooseNewDefaultIMELocked()) {
-                    Slog.i(TAG, "Can't find new IME, unsetting the current input method.");
-                    resetSelectedInputMethodAndSubtypeLocked("");
+                if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) {
+                    // Disabled input method is currently selected, switch to another one.
+                    final String selId = mSettings.getSelectedInputMethod();
+                    if (id.equals(selId) && !chooseNewDefaultIMELocked()) {
+                        Slog.i(TAG, "Can't find new IME, unsetting the current input method.");
+                        resetSelectedInputMethodAndSubtypeLocked("");
+                    }
+                } else if (id.equals(mSettings.getSelectedDefaultDeviceInputMethod())) {
+                    // Disabled default device IME while using a virtual device one, choose a
+                    // new default one but only update the settings.
+                    InputMethodInfo newDefaultIme =
+                            InputMethodInfoUtils.getMostApplicableDefaultIME(
+                                        mSettings.getEnabledInputMethodListLocked());
+                    mSettings.putSelectedDefaultDeviceInputMethod(
+                            newDefaultIme == null ? "" : newDefaultIme.getId());
                 }
                 // Previous state was enabled.
                 return true;
@@ -5652,9 +5751,8 @@
 
         @Override
         public void setVirtualDeviceInputMethodForAllUsers(int deviceId, @Nullable String imeId) {
-            // TODO(b/287269288): validate that id belongs to a valid virtual device instead.
-            Preconditions.checkArgument(deviceId != Context.DEVICE_ID_DEFAULT,
-                    "DeviceId " + deviceId + " does not belong to a virtual device.");
+            Preconditions.checkArgument(deviceId != DEVICE_ID_DEFAULT,
+                    TextUtils.formatSimple("DeviceId %d is not a virtual device id.", deviceId));
             synchronized (ImfLock.class) {
                 if (imeId == null) {
                     mVirtualDeviceMethodMap.remove(deviceId);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
index a0b55ed..f9b06de 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
@@ -51,6 +51,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 import java.util.StringJoiner;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
@@ -241,6 +242,12 @@
                 Slog.d(TAG, "--- Switch the current user from " + mCurrentUserId + " to " + userId);
             }
             mCurrentUserId = userId;
+            String ime = getSelectedInputMethod();
+            String defaultDeviceIme = getSelectedDefaultDeviceInputMethod();
+            if (defaultDeviceIme != null && !Objects.equals(ime, defaultDeviceIme)) {
+                putSelectedInputMethod(defaultDeviceIme);
+                putSelectedDefaultDeviceInputMethod(null);
+            }
         }
 
         private void putString(@NonNull String key, @Nullable String str) {
@@ -636,6 +643,24 @@
             return imi;
         }
 
+        @Nullable
+        String getSelectedDefaultDeviceInputMethod() {
+            final String imi = getString(Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null);
+            if (DEBUG) {
+                Slog.d(TAG, "getSelectedDefaultDeviceInputMethodStr: " + imi + ", "
+                        + mCurrentUserId);
+            }
+            return imi;
+        }
+
+        void putSelectedDefaultDeviceInputMethod(String imeId) {
+            if (DEBUG) {
+                Slog.d(TAG, "putSelectedDefaultDeviceInputMethodStr: " + imeId + ", "
+                        + mCurrentUserId);
+            }
+            putString(Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, imeId);
+        }
+
         void putDefaultVoiceInputMethod(String imeId) {
             if (DEBUG) {
                 Slog.d(TAG, "putDefaultVoiceInputMethodStr: " + imeId + ", " + mCurrentUserId);
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/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/InitAppsHelper.java b/services/core/java/com/android/server/pm/InitAppsHelper.java
index 41d0176..22951d5 100644
--- a/services/core/java/com/android/server/pm/InitAppsHelper.java
+++ b/services/core/java/com/android/server/pm/InitAppsHelper.java
@@ -180,7 +180,9 @@
         // priority of system overlays.
         final ArrayMap<String, File> apkInApexPreInstalledPaths = new ArrayMap<>();
         for (ApexManager.ActiveApexInfo apexInfo : mApexManager.getActiveApexInfos()) {
-            for (String packageName : mApexManager.getApksInApex(apexInfo.apexModuleName)) {
+            final String apexPackageName = mApexManager.getActivePackageNameForApexModuleName(
+                    apexInfo.apexModuleName);
+            for (String packageName : mApexManager.getApksInApex(apexPackageName)) {
                 apkInApexPreInstalledPaths.put(packageName, apexInfo.preInstalledApexPath);
             }
         }
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/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..7bd6a43 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,11 +463,32 @@
         }
     }
 
+    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) {
             cleanUpResourcesLI(codeFile, instructionSets);
         }
+        // TODO: open logging to help debug, will delete or add debug flag
+        Slog.d(TAG, "cleanUpResources for " + codeFile);
         if (packageName == null) {
             return;
         }
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/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 1577cef..2d584c4 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -98,6 +98,8 @@
 import android.view.animation.Interpolator;
 
 import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.TraceBuffer;
 import com.android.internal.util.function.pooled.PooledLambda;
@@ -297,6 +299,18 @@
         }
     }
 
+    /** It is only used by unit test. */
+    @VisibleForTesting
+    Surface forceShowMagnifierSurface(int displayId) {
+        final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
+        if (displayMagnifier != null) {
+            displayMagnifier.mMagnifedViewport.mWindow.setAlpha(DisplayMagnifier.MagnifiedViewport
+                    .ViewportWindow.AnimationController.MAX_ALPHA);
+            return displayMagnifier.mMagnifedViewport.mWindow.mSurface;
+        }
+        return null;
+    }
+
     void onWindowLayersChanged(int displayId) {
         if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
                 | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
@@ -448,6 +462,7 @@
         }
     }
 
+    // TODO(b/318327737): Remove parameter 't' when removing flag DRAW_IN_WM_LOCK.
     void drawMagnifiedRegionBorderIfNeeded(int displayId, SurfaceControl.Transaction t) {
         if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
             mAccessibilityTracing.logTrace(
@@ -1106,11 +1121,19 @@
             }
 
             void setMagnifiedRegionBorderShown(boolean shown, boolean animate) {
-                if (shown) {
+                if (ViewportWindow.DRAW_IN_WM_LOCK) {
+                    if (shown) {
+                        mFullRedrawNeeded = true;
+                        mOldMagnificationRegion.set(0, 0, 0, 0);
+                    }
+                    mWindow.setShown(shown, animate);
+                    return;
+                }
+                if (mWindow.setShown(shown, animate)) {
                     mFullRedrawNeeded = true;
+                    // Clear the old region, so recomputeBounds will refresh the current region.
                     mOldMagnificationRegion.set(0, 0, 0, 0);
                 }
-                mWindow.setShown(shown, animate);
             }
 
             void getMagnifiedFrameInContentCoords(Rect rect) {
@@ -1130,7 +1153,11 @@
 
             void drawWindowIfNeeded(SurfaceControl.Transaction t) {
                 recomputeBounds();
-                mWindow.drawIfNeeded(t);
+                if (ViewportWindow.DRAW_IN_WM_LOCK) {
+                    mWindow.drawOrRemoveIfNeeded(t);
+                    return;
+                }
+                mWindow.postDrawIfNeeded();
             }
 
             void destroyWindow() {
@@ -1158,23 +1185,28 @@
                 mWindow.dump(pw, prefix);
             }
 
-            private final class ViewportWindow {
+            private final class ViewportWindow implements Runnable {
                 private static final String SURFACE_TITLE = "Magnification Overlay";
+                // TODO(b/318327737): Remove if it is stable.
+                static final boolean DRAW_IN_WM_LOCK = !Flags.drawMagnifierBorderOutsideWmlock();
 
                 private final Region mBounds = new Region();
                 private final Rect mDirtyRect = new Rect();
                 private final Paint mPaint = new Paint();
 
                 private final SurfaceControl mSurfaceControl;
+                /** After initialization, it should only be accessed from animation thread. */
+                private final SurfaceControl.Transaction mTransaction;
                 private final BLASTBufferQueue mBlastBufferQueue;
                 private final Surface mSurface;
 
                 private final AnimationController mAnimationController;
 
                 private boolean mShown;
+                private boolean mLastSurfaceShown;
                 private int mAlpha;
 
-                private boolean mInvalidated;
+                private volatile boolean mInvalidated;
 
                 ViewportWindow(Context context) {
                     SurfaceControl surfaceControl = null;
@@ -1202,6 +1234,7 @@
                     InputMonitor.setTrustedOverlayInputInfo(mSurfaceControl, t,
                             mDisplayContent.getDisplayId(), "Magnification Overlay");
                     t.apply();
+                    mTransaction = t;
                     mSurface = mBlastBufferQueue.createSurface();
 
                     mAnimationController = new AnimationController(context,
@@ -1219,10 +1252,11 @@
                     mInvalidated = true;
                 }
 
-                void setShown(boolean shown, boolean animate) {
+                /** Returns {@code true} if the state is changed to shown. */
+                boolean setShown(boolean shown, boolean animate) {
                     synchronized (mService.mGlobalLock) {
                         if (mShown == shown) {
-                            return;
+                            return false;
                         }
                         mShown = shown;
                         mAnimationController.onFrameShownStateChanged(shown, animate);
@@ -1230,6 +1264,7 @@
                             Slog.i(LOG_TAG, "ViewportWindow shown: " + mShown);
                         }
                     }
+                    return shown;
                 }
 
                 @SuppressWarnings("unused")
@@ -1285,7 +1320,22 @@
                     mService.scheduleAnimationLocked();
                 }
 
-                void drawIfNeeded(SurfaceControl.Transaction t) {
+                void postDrawIfNeeded() {
+                    if (mInvalidated) {
+                        mService.mAnimationHandler.post(this);
+                    }
+                }
+
+                @Override
+                public void run() {
+                    drawOrRemoveIfNeeded(mTransaction);
+                }
+
+                /**
+                 * This method must only be called by animation handler directly to make sure
+                 * thread safe and there is no lock held outside.
+                 */
+                private void drawOrRemoveIfNeeded(SurfaceControl.Transaction t) {
                     // Drawing variables (alpha, dirty rect, and bounds) access is synchronized
                     // using WindowManagerGlobalLock. Grab copies of these values before
                     // drawing on the canvas so that drawing can be performed outside of the lock.
@@ -1293,6 +1343,14 @@
                     Rect drawingRect = null;
                     Region drawingBounds = null;
                     synchronized (mService.mGlobalLock) {
+                        if (!DRAW_IN_WM_LOCK && mBlastBufferQueue.mNativeObject == 0) {
+                            // Complete removal since releaseSurface has been called.
+                            if (mSurface.isValid()) {
+                                mTransaction.remove(mSurfaceControl).apply();
+                                mSurface.release();
+                            }
+                            return;
+                        }
                         if (!mInvalidated) {
                             return;
                         }
@@ -1314,6 +1372,7 @@
                         }
                     }
 
+                    final boolean showSurface;
                     // Draw without holding WindowManagerGlobalLock.
                     if (alpha > 0) {
                         Canvas canvas = null;
@@ -1329,18 +1388,38 @@
                         mPaint.setAlpha(alpha);
                         canvas.drawPath(drawingBounds.getBoundaryPath(), mPaint);
                         mSurface.unlockCanvasAndPost(canvas);
-                        t.show(mSurfaceControl);
+                        if (DRAW_IN_WM_LOCK) {
+                            t.show(mSurfaceControl);
+                            return;
+                        }
+                        showSurface = true;
                     } else {
-                        t.hide(mSurfaceControl);
+                        if (DRAW_IN_WM_LOCK) {
+                            t.hide(mSurfaceControl);
+                            return;
+                        }
+                        showSurface = false;
+                    }
+
+                    if (showSurface && !mLastSurfaceShown) {
+                        mTransaction.show(mSurfaceControl).apply();
+                        mLastSurfaceShown = true;
+                    } else if (!showSurface && mLastSurfaceShown) {
+                        mTransaction.hide(mSurfaceControl).apply();
+                        mLastSurfaceShown = false;
                     }
                 }
 
+                @GuardedBy("mService.mGlobalLock")
                 void releaseSurface() {
-                    if (mBlastBufferQueue != null) {
-                        mBlastBufferQueue.destroy();
+                    mBlastBufferQueue.destroy();
+                    if (DRAW_IN_WM_LOCK) {
+                        mService.mTransactionFactory.get().remove(mSurfaceControl).apply();
+                        mSurface.release();
+                        return;
                     }
-                    mService.mTransactionFactory.get().remove(mSurfaceControl).apply();
-                    mSurface.release();
+                    // Post to perform cleanup on the thread which handles mSurface.
+                    mService.mAnimationHandler.post(this);
                 }
 
                 void dump(PrintWriter pw, String prefix) {
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/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/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/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index e0232b1..14dc0eb 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -32,6 +32,7 @@
 import android.annotation.Nullable;
 import android.app.AppGlobals;
 import android.app.BroadcastOptions;
+import android.app.admin.BooleanPolicyValue;
 import android.app.admin.DevicePolicyIdentifiers;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.DevicePolicyState;
@@ -142,6 +143,67 @@
         mAdminPolicySize = new SparseArray<>();
     }
 
+    private void maybeForceEnforcementRefreshLocked(@NonNull PolicyDefinition<?> policyDefinition) {
+        try {
+            if (shouldForceEnforcementRefresh(policyDefinition)) {
+                // This is okay because it's only true for user restrictions which are all <Boolean>
+                forceEnforcementRefreshLocked((PolicyDefinition<Boolean>) policyDefinition);
+            }
+        } catch (Throwable e) {
+            // Catch any possible exceptions just to be on the safe side
+            Log.e(TAG, "Exception throw during maybeForceEnforcementRefreshLocked", e);
+        }
+    }
+
+    private boolean shouldForceEnforcementRefresh(@NonNull PolicyDefinition<?> policyDefinition) {
+        // These are all "not nullable" but for the purposes of maximum safety for a lightly tested
+        // change we check here
+        if (policyDefinition == null) {
+            return false;
+        }
+        PolicyKey policyKey = policyDefinition.getPolicyKey();
+        if (policyKey == null) {
+            return false;
+        }
+
+        if (policyKey instanceof UserRestrictionPolicyKey) {
+            // b/307481299 We must force all user restrictions to re-sync local
+            // + global on each set/clear
+            return true;
+        }
+
+        return false;
+    }
+
+    private void forceEnforcementRefreshLocked(PolicyDefinition<Boolean> policyDefinition) {
+        Binder.withCleanCallingIdentity(() -> {
+            // Sync global state
+            PolicyValue<Boolean> globalValue = new BooleanPolicyValue(false);
+            try {
+                PolicyState<Boolean> policyState = getGlobalPolicyStateLocked(policyDefinition);
+                globalValue = policyState.getCurrentResolvedPolicy();
+            } catch (IllegalArgumentException e) {
+                // Expected for local-only policies
+            }
+
+            enforcePolicy(policyDefinition, globalValue, UserHandle.USER_ALL);
+
+            // Loop through each user and sync that user's state
+            for (UserInfo user : mUserManager.getUsers()) {
+                PolicyValue<Boolean> localValue = new BooleanPolicyValue(false);
+                try {
+                    PolicyState<Boolean> localPolicyState = getLocalPolicyStateLocked(
+                            policyDefinition, user.id);
+                    localValue = localPolicyState.getCurrentResolvedPolicy();
+                } catch (IllegalArgumentException e) {
+                    // Expected for global-only policies
+                }
+
+                enforcePolicy(policyDefinition, localValue, user.id);
+            }
+        });
+    }
+
     /**
      * Set the policy for the provided {@code policyDefinition} (see {@link PolicyDefinition}) and
      * {@code enforcingAdmin} to the provided {@code value}.
@@ -188,6 +250,7 @@
             // No need to notify admins as no new policy is actually enforced, we're just filling in
             // the data structures.
             if (!skipEnforcePolicy) {
+                maybeForceEnforcementRefreshLocked(policyDefinition);
                 if (policyChanged) {
                     onLocalPolicyChangedLocked(policyDefinition, enforcingAdmin, userId);
                 }
@@ -278,6 +341,7 @@
         Objects.requireNonNull(enforcingAdmin);
 
         synchronized (mLock) {
+            maybeForceEnforcementRefreshLocked(policyDefinition);
             if (!hasLocalPolicyLocked(policyDefinition, userId)) {
                 return;
             }
@@ -451,6 +515,7 @@
             // No need to notify admins as no new policy is actually enforced, we're just filling in
             // the data structures.
             if (!skipEnforcePolicy) {
+                maybeForceEnforcementRefreshLocked(policyDefinition);
                 if (policyChanged) {
                     onGlobalPolicyChangedLocked(policyDefinition, enforcingAdmin);
                 }
@@ -506,6 +571,7 @@
 
             boolean policyChanged = policyState.removePolicy(enforcingAdmin);
 
+            maybeForceEnforcementRefreshLocked(policyDefinition);
             if (policyChanged) {
                 onGlobalPolicyChangedLocked(policyDefinition, enforcingAdmin);
             }
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/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index 113511e..321d945 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -73,6 +73,7 @@
         // TODO: remove once Android migrates to JUnit 4.12, which provides assertThrows
         "testng",
         "compatibility-device-util-axt",
+        "flag-junit",
     ],
 
     libs: [
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
index 57326b2..d08cdc7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
@@ -343,10 +343,12 @@
         List<ApexManager.ScanResult> scanResults = scanApexInfos(apexInfo);
         mApexManager.notifyScanResult(scanResults);
 
-        assertThat(mApexManager.getApkInApexInstallError(activeApex.apexModuleName)).isNull();
+        final String apexPackageName = mApexManager.getActivePackageNameForApexModuleName(
+                activeApex.apexModuleName);
+        assertThat(mApexManager.getApkInApexInstallError(apexPackageName)).isNull();
         mApexManager.reportErrorWithApkInApex(activeApex.apexDirectory.getAbsolutePath(),
                 "Some random error");
-        assertThat(mApexManager.getApkInApexInstallError(activeApex.apexModuleName))
+        assertThat(mApexManager.getApkInApexInstallError(apexPackageName))
                 .isEqualTo("Some random error");
     }
 
@@ -370,9 +372,11 @@
         List<ApexManager.ScanResult> scanResults = scanApexInfos(apexInfo);
         mApexManager.notifyScanResult(scanResults);
 
-        assertThat(mApexManager.getApksInApex(activeApex.apexModuleName)).isEmpty();
+        final String apexPackageName = mApexManager.getActivePackageNameForApexModuleName(
+                activeApex.apexModuleName);
+        assertThat(mApexManager.getApksInApex(apexPackageName)).isEmpty();
         mApexManager.registerApkInApex(fakeApkInApex);
-        assertThat(mApexManager.getApksInApex(activeApex.apexModuleName)).isEmpty();
+        assertThat(mApexManager.getApksInApex(apexPackageName)).isEmpty();
     }
 
     @Test
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/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/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index 8bf4833..21fee72 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -37,6 +37,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
@@ -85,6 +86,7 @@
 import android.view.InputChannel;
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
+import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.View;
 import android.view.WindowInsets;
@@ -952,6 +954,57 @@
     }
 
     @Test
+    public void testDrawMagnifiedViewport() {
+        final int displayId = mDisplayContent.mDisplayId;
+        // Use real surface, so ViewportWindow's BlastBufferQueue can be created.
+        final ArrayList<SurfaceControl> surfaceControls = new ArrayList<>();
+        mWm.mSurfaceControlFactory = s -> new SurfaceControl.Builder() {
+            @Override
+            public SurfaceControl build() {
+                final SurfaceControl sc = super.build();
+                surfaceControls.add(sc);
+                return sc;
+            }
+        };
+        mWm.mAccessibilityController.setMagnificationCallbacks(displayId,
+                mock(WindowManagerInternal.MagnificationCallbacks.class));
+        final boolean[] lockCanvasInWmLock = { false };
+        final Surface surface = mWm.mAccessibilityController.forceShowMagnifierSurface(displayId);
+        spyOn(surface);
+        doAnswer(invocationOnMock -> {
+            lockCanvasInWmLock[0] |= Thread.holdsLock(mWm.mGlobalLock);
+            invocationOnMock.callRealMethod();
+            return null;
+        }).when(surface).lockCanvas(any());
+        mWm.mAccessibilityController.drawMagnifiedRegionBorderIfNeeded(displayId, mTransaction);
+        waitUntilHandlersIdle();
+        try {
+            verify(surface).lockCanvas(any());
+
+            clearInvocations(surface);
+            // Invalidate and redraw.
+            mWm.mAccessibilityController.onDisplaySizeChanged(mDisplayContent);
+            mWm.mAccessibilityController.drawMagnifiedRegionBorderIfNeeded(displayId, mTransaction);
+            // Turn off magnification to release surface.
+            mWm.mAccessibilityController.setMagnificationCallbacks(displayId, null);
+            if (!com.android.window.flags.Flags.drawMagnifierBorderOutsideWmlock()) {
+                verify(surface).release();
+                assertTrue(lockCanvasInWmLock[0]);
+                return;
+            }
+            waitUntilHandlersIdle();
+            // lockCanvas must not be called after releasing.
+            verify(surface, never()).lockCanvas(any());
+            verify(surface).release();
+            assertFalse(lockCanvasInWmLock[0]);
+        } finally {
+            for (int i = surfaceControls.size() - 1; i >= 0; --i) {
+                surfaceControls.get(i).release();
+            }
+        }
+    }
+
+    @Test
     public void testRequestKeyboardShortcuts_noWindow() {
         doNothing().when(mWm.mContext).enforceCallingOrSelfPermission(anyString(), anyString());
         doReturn(null).when(mWm).getFocusedWindowLocked();
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 9e292be..1b47dfe 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -510,7 +510,7 @@
     /** @hide */
     @UnsupportedAppUsage
     public TelephonyManager(Context context) {
-      this(context, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
+        this(context, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
     }
 
     /** @hide */
@@ -2140,10 +2140,14 @@
      * the IMEI/SV for GSM phones. Return null if the software version is
      * not available.
      * <p>
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY}.
      */
     @RequiresPermission(anyOf = {
             android.Manifest.permission.READ_PHONE_STATE,
             android.Manifest.permission.READ_BASIC_PHONE_STATE})
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
     @Nullable
     public String getDeviceSoftwareVersion() {
         return getDeviceSoftwareVersion(getSlotIndex());
@@ -2158,10 +2162,13 @@
      *
      * @param slotIndex of which deviceID is returned
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY}.
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
     @Nullable
     public String getDeviceSoftwareVersion(int slotIndex) {
         ITelephony telephony = getITelephony();
@@ -2288,6 +2295,9 @@
      *
      * See {@link #getImei(int)} for details on the required permissions and behavior
      * when the caller does not hold sufficient permissions.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_GSM}.
      */
     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -2330,6 +2340,9 @@
      * </ul>
      *
      * @param slotIndex of which IMEI is returned
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_GSM}.
      */
     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -2350,6 +2363,9 @@
     /**
      * Returns the Type Allocation Code from the IMEI. Return null if Type Allocation Code is not
      * available.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_GSM}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_GSM)
     @Nullable
@@ -2362,6 +2378,9 @@
      * available.
      *
      * @param slotIndex of which Type Allocation Code is returned
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_GSM}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_GSM)
     @Nullable
@@ -2407,6 +2426,9 @@
      *     the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or
      *     higher, then a SecurityException is thrown.</li>
      * </ul>
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
      */
     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -2446,6 +2468,9 @@
      * </ul>
      *
      * @param slotIndex of which MEID is returned
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
      */
     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -2472,6 +2497,9 @@
     /**
      * Returns the Manufacturer Code from the MEID. Return null if Manufacturer Code is not
      * available.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
     @Nullable
@@ -2484,6 +2512,9 @@
      * available.
      *
      * @param slotIndex of which Type Allocation Code is returned
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
     @Nullable
@@ -2528,6 +2559,9 @@
      *     the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or
      *     higher, then a SecurityException is thrown.</li>
      * </ul>
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -2563,10 +2597,14 @@
      *<p>
      * @return Current location of the device or null if not available.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     *
      * @deprecated use {@link #getAllCellInfo} instead, which returns a superset of this API.
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
     public CellLocation getCellLocation() {
         try {
             ITelephony telephony = getITelephony();
@@ -2596,12 +2634,15 @@
      *
      * @return List of NeighboringCellInfo or null if info unavailable.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @removed
      * @deprecated Use {@link #getAllCellInfo} which returns a superset of the information
      *             from NeighboringCellInfo, including LTE cell information.
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
     public List<NeighboringCellInfo> getNeighboringCellInfo() {
         try {
             ITelephony telephony = getITelephony();
@@ -2648,9 +2689,12 @@
      * @see #PHONE_TYPE_CDMA
      * @see #PHONE_TYPE_SIP
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY}.
      * {@hide}
      */
     @SystemApi
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
     public int getCurrentPhoneType() {
         return getCurrentPhoneType(getSubId());
     }
@@ -2663,9 +2707,13 @@
      * @see #PHONE_TYPE_CDMA
      *
      * @param subId for which phone type is returned
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY}.
      * @hide
      */
     @SystemApi
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
     public int getCurrentPhoneType(int subId) {
         int phoneId;
         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
@@ -2712,7 +2760,11 @@
      * @see #PHONE_TYPE_GSM
      * @see #PHONE_TYPE_CDMA
      * @see #PHONE_TYPE_SIP
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY}.
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
     public int getPhoneType() {
         if (!isVoiceCapable()) {
             return PHONE_TYPE_NONE;
@@ -2912,6 +2964,9 @@
      * @see CarrierConfigManager#getConfigForSubId(int)
      * @see #createForSubscriptionId(int)
      * @see #createForPhoneAccountHandle(PhoneAccountHandle)
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @WorkerThread
@@ -2958,6 +3013,9 @@
      * <p>
      * @return the lowercase 2 character ISO-3166-1 alpha-2 country code, or empty string if not
      * available.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
     public String getNetworkCountryIso() {
@@ -2980,6 +3038,8 @@
      * available.
      *
      * @throws IllegalArgumentException when the slotIndex is invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      *
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
@@ -3108,9 +3168,13 @@
      *
      * @deprecated use {@link #getDataNetworkType()}
      * @return the NETWORK_TYPE_xxxx for current data connection.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
     public @NetworkType int getNetworkType() {
         return getNetworkType(getSubId(SubscriptionManager.getActiveDataSubscriptionId()));
     }
@@ -3199,12 +3263,15 @@
      * @see #NETWORK_TYPE_EHRPD
      * @see #NETWORK_TYPE_HSPAP
      * @see #NETWORK_TYPE_NR
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(anyOf = {
             android.Manifest.permission.READ_PHONE_STATE,
             android.Manifest.permission.READ_BASIC_PHONE_STATE})
-    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
     public @NetworkType int getDataNetworkType() {
         return getDataNetworkType(getSubId(SubscriptionManager.getActiveDataSubscriptionId()));
     }
@@ -3245,6 +3312,9 @@
      * or {@link android.Manifest.permission#READ_BASIC_PHONE_STATE
      * READ_BASIC_PHONE_STATE} or that the calling app has carrier privileges
      * (see {@link #hasCarrierPrivileges}).
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(anyOf = {
@@ -3597,6 +3667,9 @@
      * of whether an active SIM profile is present or not so this API would always return true.
      *
      * @return true if a ICC card is present.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public boolean hasIccCard() {
@@ -3640,6 +3713,9 @@
      * @see #SIM_STATE_PERM_DISABLED
      * @see #SIM_STATE_CARD_IO_ERROR
      * @see #SIM_STATE_CARD_RESTRICTED
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public @SimState int getSimState() {
@@ -3681,6 +3757,8 @@
      * @see #SIM_STATE_CARD_RESTRICTED
      * @see #SIM_STATE_PRESENT
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -3701,11 +3779,14 @@
      * @see #SIM_STATE_CARD_RESTRICTED
      * @see #SIM_STATE_PRESENT
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      * @deprecated instead use {@link #getSimCardState(int, int)}
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     @Deprecated
     public @SimState int getSimCardState(int physicalSlotIndex) {
         int activePort = getFirstActivePortIndex(physicalSlotIndex);
@@ -3727,6 +3808,8 @@
      * @see #SIM_STATE_CARD_RESTRICTED
      * @see #SIM_STATE_PRESENT
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -3785,6 +3868,8 @@
      * @see #SIM_STATE_PERM_DISABLED
      * @see #SIM_STATE_LOADED
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -3808,11 +3893,14 @@
      * @see #SIM_STATE_PERM_DISABLED
      * @see #SIM_STATE_LOADED
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      * @deprecated instead use {@link #getSimApplicationState(int, int)}
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     @Deprecated
     public @SimState int getSimApplicationState(int physicalSlotIndex) {
         int activePort = getFirstActivePortIndex(physicalSlotIndex);
@@ -3836,6 +3924,8 @@
      * @see #SIM_STATE_PERM_DISABLED
      * @see #SIM_STATE_LOADED
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -3876,6 +3966,9 @@
      *
      * @param appType the uicc app type like {@link APPTYPE_CSIM}
      * @return true if the specified type of application in UICC CARD or false if no uicc or error.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -3908,6 +4001,9 @@
      * @see #SIM_STATE_PERM_DISABLED
      * @see #SIM_STATE_CARD_IO_ERROR
      * @see #SIM_STATE_CARD_RESTRICTED
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public @SimState int getSimState(int slotIndex) {
@@ -4105,6 +4201,9 @@
      *     the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or
      *     higher, then a SecurityException is thrown.</li>
      * </ul>
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -4172,6 +4271,9 @@
      *
      * @return {@code true} if 3GPP and 3GPP2 radio technologies can be supported at the same time
      *         {@code false} if not supported or unknown
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @SystemApi
@@ -4219,6 +4321,9 @@
      * through a factory reset.
      *
      * @return card ID of the default eUICC card, if loaded.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_EUICC}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_EUICC)
     public int getCardIdForDefaultEuicc() {
@@ -4252,6 +4357,9 @@
      * @return a list of UiccCardInfo objects, representing information on the currently inserted
      * UICCs and eUICCs. Each UiccCardInfo in the list will have private information filtered out if
      * the caller does not have adequate permissions for that card.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@@ -4276,6 +4384,8 @@
      *
      * @return UiccSlotInfo array.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -4319,6 +4429,9 @@
      * @param physicalSlots The content of the array represents the physical slot index. The array
      *        size should be same as {@link #getUiccSlotsInfo()}.
      * @return boolean Return true if the switch succeeds, false if the switch fails.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      * @deprecated {@link #setSimSlotMapping(Collection, Executor, Consumer)}
      */
@@ -4328,6 +4441,7 @@
     @SystemApi
     @Deprecated
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public boolean switchSlots(int[] physicalSlots) {
         try {
             ITelephony telephony = getITelephony();
@@ -4420,6 +4534,8 @@
      * @throws IllegalArgumentException if the caller passes in an invalid collection of
      *         UiccSlotMapping like duplicate data, etc
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -4452,11 +4568,14 @@
      * @return a map indicates the mapping from logical slots to physical slots. The size of the map
      * should be {@link #getPhoneCount()} if success, otherwise return an empty map.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      * @deprecated use {@link #getSimSlotMapping()} instead.
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     @NonNull
     @Deprecated
     public Map<Integer, Integer> getLogicalToPhysicalSlotMapping() {
@@ -4484,6 +4603,9 @@
      *
      * @return a collection of {@link UiccSlotMapping} which indicates the mapping from logical
      *         slots to ports and physical slots.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -4541,6 +4663,9 @@
      *     the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or
      *     higher, then a SecurityException is thrown.</li>
      * </ul>
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -4593,6 +4718,8 @@
      *         found, and the carrier does not require a key.
      * @throws IllegalArgumentException when an invalid key is found or when key is required but
      *         not found.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -4638,6 +4765,9 @@
      * Requires Permission: MODIFY_PHONE_STATE.
      *
      * @see #getCarrierInfoForImsiEncryption
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -4840,6 +4970,9 @@
      *                 from disk, as well as on which {@code callback} will be called.
      * @param callback A callback called when the upload operation terminates, either in success
      *                 or in error.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
     public void uploadCallComposerPicture(@NonNull Path pictureToUpload,
@@ -4947,6 +5080,9 @@
      *                 read, as well as on which the callback will be called.
      * @param callback A callback called when the upload operation terminates, either in success
      *                 or in error.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
     public void uploadCallComposerPicture(@NonNull InputStream pictureToUpload,
@@ -5081,6 +5217,9 @@
      *
      * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
      * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @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)
@@ -5139,6 +5278,8 @@
      *     {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
      *     for apps targeting SDK API level 29 and below.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @deprecated use {@link SubscriptionManager#getPhoneNumber(int)} instead.
      */
     @Deprecated
@@ -5148,6 +5289,7 @@
             android.Manifest.permission.READ_SMS,
             android.Manifest.permission.READ_PHONE_NUMBERS
     })
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public String getLine1Number() {
         return getLine1Number(getSubId());
     }
@@ -5214,9 +5356,13 @@
      * @param alphaTag alpha-tagging of the dailing nubmer
      * @param number The dialing number
      * @return true if the operation was executed correctly.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @deprecated use {@link SubscriptionManager#setCarrierPhoneNumber(int, String)} instead.
      */
     @Deprecated
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public boolean setLine1NumberForDisplay(String alphaTag, String number) {
         return setLine1NumberForDisplay(getSubId(), alphaTag, number);
     }
@@ -5336,6 +5482,8 @@
      * {@link SubscriptionManager#createSubscriptionGroup(List)} for the definition of a group,
      * otherwise return an empty array if there is a failure.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -5421,6 +5569,9 @@
      *
      * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
      * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@@ -5459,6 +5610,9 @@
      *
      * @param alphaTag The alpha tag to display.
      * @param number The voicemail number.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
     public boolean setVoiceMailNumber(String alphaTag, String number) {
@@ -5533,6 +5687,8 @@
      * @see #KEY_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL
      * @see #KEY_VOICEMAIL_SCRAMBLED_PIN_STRING
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      * @hide
      */
     @SystemApi
@@ -5563,6 +5719,9 @@
      * @see #createForSubscriptionId(int)
      * @see #createForPhoneAccountHandle(PhoneAccountHandle)
      * @see VisualVoicemailService
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @Nullable
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@@ -5593,6 +5752,9 @@
      *
      * @see TelecomManager#getDefaultDialerPackage()
      * @see CarrierConfigManager#KEY_CARRIER_VVM_PACKAGE_NAME_STRING_ARRAY
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
     public void setVisualVoicemailSmsFilterSettings(VisualVoicemailSmsFilterSettings settings) {
@@ -5623,6 +5785,9 @@
      *
      * @see SmsManager#sendDataMessage(String, String, short, byte[], PendingIntent, PendingIntent)
      * @see SmsManager#sendTextMessage(String, String, String, PendingIntent, PendingIntent)
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
     public void sendVisualVoicemailSms(String number, int port, String text,
@@ -5808,6 +5973,9 @@
       * @see #SIM_ACTIVATION_STATE_ACTIVATING
       * @see #SIM_ACTIVATION_STATE_ACTIVATED
       * @see #SIM_ACTIVATION_STATE_DEACTIVATED
+      *
+      * @throws UnsupportedOperationException If the device does not have
+      *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}
       * @hide
       */
     @SystemApi
@@ -5856,6 +6024,9 @@
      * @see #SIM_ACTIVATION_STATE_ACTIVATED
      * @see #SIM_ACTIVATION_STATE_DEACTIVATED
      * @see #SIM_ACTIVATION_STATE_RESTRICTED
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      * @hide
      */
     @SystemApi
@@ -5904,6 +6075,9 @@
      * @see #SIM_ACTIVATION_STATE_ACTIVATING
      * @see #SIM_ACTIVATION_STATE_ACTIVATED
      * @see #SIM_ACTIVATION_STATE_DEACTIVATED
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      * @hide
      */
     @SystemApi
@@ -5954,6 +6128,9 @@
      * @see #SIM_ACTIVATION_STATE_ACTIVATED
      * @see #SIM_ACTIVATION_STATE_DEACTIVATED
      * @see #SIM_ACTIVATION_STATE_RESTRICTED
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      * @hide
      */
     @SystemApi
@@ -6032,6 +6209,9 @@
      *
      * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
      * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@@ -6072,7 +6252,10 @@
      *
      * @throws SecurityException if the caller does not have carrier privileges or is not the
      *         current default dialer
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
     public void sendDialerSpecialCode(String inputCode) {
         try {
             final ITelephony telephony = getITelephony();
@@ -6143,6 +6326,9 @@
      *
      * <p>Requires Permission:
      * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @Nullable
@@ -6168,6 +6354,9 @@
      * Returns the IMS public user identities (IMPU) that were loaded from the ISIM.
      * @return an array of IMPU strings, with one IMPU per string, or null if
      *      not present or not loaded
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      * @deprecated use {@link #getImsPublicUserIdentities()}
      */
@@ -6199,6 +6388,8 @@
      *         EF_IMPU is not available.
      * @throws IllegalStateException in case the ISIM hasn’t been loaded
      * @throws SecurityException if the caller does not have the required permission/privilege
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @NonNull
@@ -6254,6 +6445,10 @@
      * targeting API level 31+.
      *
      * @return the current call state.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELECOM}.
+     *
      * @deprecated Use {@link #getCallStateForSubscription} to retrieve the call state for a
      * specific telephony subscription (which allows carrier privileged apps),
      * {@link TelephonyCallback.CallStateListener} for real-time call state updates, or
@@ -6261,6 +6456,7 @@
      * device.
      */
     @RequiresPermission(value = android.Manifest.permission.READ_PHONE_STATE, conditional = true)
+    @RequiresFeature(PackageManager.FEATURE_TELECOM)
     @Deprecated
     public @CallState int getCallState() {
         if (mContext != null) {
@@ -6281,6 +6477,9 @@
      * @see TelephonyManager#createForSubscriptionId(int)
      * @see TelephonyManager#createForPhoneAccountHandle(PhoneAccountHandle)
      * @return The call state of the subscription associated with this TelephonyManager instance.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
@@ -6341,6 +6540,9 @@
      * @see #DATA_ACTIVITY_OUT
      * @see #DATA_ACTIVITY_INOUT
      * @see #DATA_ACTIVITY_DORMANT
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
     public int getDataActivity() {
@@ -6414,6 +6616,9 @@
      * @see #DATA_SUSPENDED
      * @see #DATA_DISCONNECTING
      * @see #DATA_HANDOVER_IN_PROGRESS
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
     public int getDataState() {
@@ -6599,10 +6804,14 @@
      * Returns the CDMA ERI icon display number. The number is assigned by
      * 3GPP2 C.R1001-H v1.0 Table 8.1-1. Additionally carriers define their own ERI display numbers.
      * Defined values are {@link #ERI_ON}, {@link #ERI_OFF}, and {@link #ERI_FLASH}.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
     public @EriIconIndex int getCdmaEnhancedRoamingIndicatorDisplayNumber() {
         return getCdmaEriIconIndex(getSubId());
     }
@@ -6810,6 +7019,9 @@
      *
      * @return List of {@link android.telephony.CellInfo}; null if cell
      * information is unavailable.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
@@ -6905,6 +7117,9 @@
      *
      * @param executor the executor on which callback will be invoked.
      * @param callback a callback to receive CellInfo.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
@@ -6966,6 +7181,9 @@
      * @param workSource the requestor to whom the power consumption for this should be attributed.
      * @param executor the executor on which callback will be invoked.
      * @param callback a callback to receive CellInfo.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @SystemApi
@@ -7052,6 +7270,9 @@
 
     /**
      * Returns the MMS user agent.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public String getMmsUserAgent() {
@@ -7068,6 +7289,9 @@
 
     /**
      * Returns the MMS user agent profile URL.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public String getMmsUAProfUrl() {
@@ -7111,8 +7335,12 @@
      *
      * @param AID Application id. See ETSI 102.221 and 101.220.
      * @return an IccOpenLogicalChannelResponse object.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @deprecated Replaced by {@link #iccOpenLogicalChannel(String, int)}
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     @Deprecated
     public IccOpenLogicalChannelResponse iccOpenLogicalChannel(String AID) {
         return iccOpenLogicalChannel(getSubId(), AID, -1);
@@ -7145,6 +7373,8 @@
      * @param aid Application id. See ETSI 102.221 and 101.220.
      * @param p2 P2 parameter (described in ISO 7816-4).
      * @return an IccOpenLogicalChannelResponse object.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      * @deprecated This API is not compatible on eUICC supporting Multiple Enabled Profile(MEP),
      * instead use {@link #iccOpenLogicalChannelByPort(int, int, String, int)}
@@ -7200,9 +7430,13 @@
      * @param aid Application id. See ETSI 102.221 and 101.220.
      * @param p2 P2 parameter (described in ISO 7816-4).
      * @return an IccOpenLogicalChannelResponse object.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     @SystemApi
     @NonNull
     public IccOpenLogicalChannelResponse iccOpenLogicalChannelByPort(int slotIndex,
@@ -7255,6 +7489,9 @@
      * @param AID Application id. See ETSI 102.221 and 101.220.
      * @param p2 P2 parameter (described in ISO 7816-4).
      * @return an IccOpenLogicalChannelResponse object.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public IccOpenLogicalChannelResponse iccOpenLogicalChannel(String AID, int p2) {
@@ -7321,6 +7558,9 @@
      * @param channel is the channel id to be closed as returned by a successful
      *            iccOpenLogicalChannel.
      * @return true if the channel was closed successfully.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      * @deprecated This API is not compatible on eUICC supporting Multiple Enabled Profile(MEP),
      * instead use {@link #iccCloseLogicalChannelByPort(int, int, int)}
@@ -7365,9 +7605,12 @@
      * @throws IllegalStateException if the Telephony process is not currently available or modem
      *                               currently can't process this command.
      * @throws IllegalArgumentException if invalid arguments are passed.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     @SystemApi
     public void iccCloseLogicalChannelByPort(int slotIndex, int portIndex, int channel) {
         try {
@@ -7403,6 +7646,8 @@
      *            iccOpenLogicalChannel.
      * @return true if the channel was closed successfully.
      * @throws IllegalArgumentException if input parameters are wrong. e.g., invalid channel
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public boolean iccCloseLogicalChannel(int channel) {
@@ -7469,6 +7714,8 @@
      * @param data Data to be sent with the APDU.
      * @return The APDU response from the ICC card with the status appended at the end, or null if
      * there is an issue connecting to the Telephony service.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      * @deprecated This API is not compatible on eUICC supporting Multiple Enabled Profile(MEP),
      * instead use
@@ -7516,9 +7763,13 @@
      * @param data Data to be sent with the APDU.
      * @return The APDU response from the ICC card with the status appended at the end, or null if
      * there is an issue connecting to the Telephony service.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     @SystemApi
     @NonNull
     public String iccTransmitApduLogicalChannelByPort(int slotIndex, int portIndex, int channel,
@@ -7563,6 +7814,9 @@
      * @param data Data to be sent with the APDU.
      * @return The APDU response from the ICC card with the status appended at
      *            the end.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public String iccTransmitApduLogicalChannel(int channel, int cla,
@@ -7628,6 +7882,9 @@
      * @param data Data to be sent with the APDU.
      * @return The APDU response from the ICC card with the status appended at
      *            the end.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      * @deprecated This API is not compatible on eUICC supporting Multiple Enabled Profile(MEP),
      * instead use
@@ -7673,9 +7930,13 @@
      * @param data Data to be sent with the APDU.
      * @return The APDU response from the ICC card with the status appended at
      *            the end.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     @SystemApi
     @NonNull
     public String iccTransmitApduBasicChannelByPort(int slotIndex, int portIndex, int cla,
@@ -7712,6 +7973,9 @@
      * @param data Data to be sent with the APDU.
      * @return The APDU response from the ICC card with the status appended at
      *            the end.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public String iccTransmitApduBasicChannel(int cla,
@@ -7768,6 +8032,9 @@
      * @param p3 P3 value of the APDU command.
      * @param filePath
      * @return The APDU response.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public byte[] iccExchangeSimIO(int fileID, int command, int p1, int p2, int p3,
@@ -7817,6 +8084,9 @@
      * @return The APDU response from the ICC card in hexadecimal format
      *         with the last 4 bytes being the status word. If the command fails,
      *         returns an empty string.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public String sendEnvelopeWithStatus(String content) {
@@ -7978,6 +8248,8 @@
      *
      * @return {@code true} on success; {@code false} on any failure.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@@ -8008,6 +8280,8 @@
      *
      * @deprecated  Using {@link #rebootModem()} instead.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@@ -8035,6 +8309,8 @@
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      * @throws IllegalStateException if the Telephony process is not currently available.
      * @throws RuntimeException
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
@@ -8166,6 +8442,9 @@
      *                      {@link #getMaxNumberVerificationTimeoutMillis()}, whichever is lesser.
      * @param executor The {@link Executor} that callbacks should be executed on.
      * @param callback The callback to use for delivering results.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@@ -8378,6 +8657,9 @@
      * See 3GPP TS 31.103 (Section 4.2.7) for the definition and more information on this table.
      *
      * @return IMS Service Table or null if not present or not loaded
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @Nullable
@@ -8496,6 +8778,9 @@
      *   Key freshness failure
      *   Authentication error, no memory space available
      *   Authentication error, no memory space available in EFMUK
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     // TODO(b/73660190): This should probably require MODIFY_PHONE_STATE, not
     // READ_PRIVILEGED_PHONE_STATE. It certainly shouldn't reference the permission in Javadoc since
@@ -8552,6 +8837,9 @@
      * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @return an array of forbidden PLMNs or null if not available
+     *
+     * @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)
@@ -8602,6 +8890,9 @@
      * @return number of PLMNs that were successfully written to the SIM FPLMN list.
      * This may be less than the number of PLMNs passed in where the SIM file does not have enough
      * room for all of the values passed in. Return -1 in the event of an unexpected failure
+     *
+     * @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)
@@ -8637,6 +8928,8 @@
      * @param appType of type int of either {@link #APPTYPE_USIM} or {@link #APPTYPE_ISIM}.
      * @return HexString represents sim service table else null.
      * @throws SecurityException if the caller does not have the required permission/privileges
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
 
@@ -8673,6 +8966,9 @@
      * state.
      *
      * @param slotIndex the sim slot to reset the IMS stack on.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      * @hide */
     @SystemApi
     @WorkerThread
@@ -9084,12 +9380,15 @@
      *
      * @return The bitmask of preferred network types.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      * @deprecated Use {@link #getAllowedNetworkTypesBitmask} instead.
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     @SystemApi
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
     public @NetworkTypeBitMask long getPreferredNetworkTypeBitmask() {
         return getAllowedNetworkTypesBitmask();
     }
@@ -9108,6 +9407,8 @@
      *
      * @return The bitmask of allowed network types.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -9133,10 +9434,14 @@
      * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @return the allowed network type bitmask
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      * @deprecated Use {@link #getAllowedNetworkTypesForReason} instead.
      */
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
     @SystemApi
     @Deprecated
     public @NetworkTypeBitMask long getAllowedNetworkTypes() {
@@ -9161,6 +9466,9 @@
      * <p>Requires Permission:
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -9247,6 +9555,9 @@
      *        tasks one at a time in serial order.
      * @param callback Returns network scan results or errors.
      * @return A NetworkScan obj which contains a callback which can be used to stop the scan.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(allOf = {
@@ -9295,11 +9606,15 @@
      *        tasks one at a time in serial order.
      * @param callback Returns network scan results or errors.
      * @return A NetworkScan obj which contains a callback which can be used to stop the scan.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(allOf = {
             android.Manifest.permission.MODIFY_PHONE_STATE
     })
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
     public @Nullable NetworkScan requestNetworkScan(
             @IncludeLocationData int includeLocationData,
             @NonNull NetworkScanRequest request,
@@ -9317,6 +9632,9 @@
     }
 
     /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     *
      * @deprecated
      * Use {@link
      * #requestNetworkScan(NetworkScanRequest, Executor, TelephonyScanManager.NetworkScanCallback)}
@@ -9327,6 +9645,7 @@
             android.Manifest.permission.MODIFY_PHONE_STATE,
             Manifest.permission.ACCESS_FINE_LOCATION
     })
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
     public NetworkScan requestNetworkScan(
         NetworkScanRequest request, TelephonyScanManager.NetworkScanCallback callback) {
         return requestNetworkScan(request, AsyncTask.SERIAL_EXECUTOR, callback);
@@ -9347,6 +9666,9 @@
      * attaching to the selected PLMN until reboot; otherwise, attach to the chosen PLMN and resume
      * normal network selection next time.
      * @return {@code true} on success; {@code false} on any failure.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -9378,6 +9700,9 @@
      *         {@link AccessNetworkConstants.AccessNetworkType#UNKNOWN}, modem will select
      *         the next best RAN for network registration.
      * @return {@code true} on success; {@code false} on any failure.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
@@ -9430,6 +9755,9 @@
      * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @return the network selection mode.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @SuppressAutoDoc // No support for carrier privileges (b/72967236).
     @RequiresPermission(anyOf = {
@@ -9459,6 +9787,9 @@
      * (see {@link #hasCarrierPrivileges})
      *
      * @return manually selected network info on success or empty string on failure
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @SuppressAutoDoc // No support carrier privileges (b/72967236).
     @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
@@ -9488,6 +9819,8 @@
      *
      * @return {@code true} if this device is in emergency SMS mode, {@code false} otherwise.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      * @hide
      */
     @SystemApi
@@ -9557,11 +9890,15 @@
      *
      * @param networkTypeBitmask The bitmask of preferred network types.
      * @return true on success; false on any failure.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      * @deprecated Use {@link #setAllowedNetworkTypesForReason} instead.
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
     @SystemApi
     public boolean setPreferredNetworkTypeBitmask(@NetworkTypeBitMask long networkTypeBitmask) {
         try {
@@ -9603,6 +9940,10 @@
      *
      * @param allowedNetworkTypes The bitmask of allowed network types.
      * @return true on success; false on any failure.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     *
      * @hide
      * @deprecated Use {@link #setAllowedNetworkTypesForReason} instead with reason
      * {@link #ALLOWED_NETWORK_TYPES_REASON_CARRIER}.
@@ -9690,11 +10031,16 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      * @throws IllegalArgumentException if invalid AllowedNetworkTypesReason is passed.
      * @throws SecurityException if the caller does not have the required privileges or if the
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * caller tries to use one of the following security-based reasons without
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE} permissions.
      * <ol>
      *     <li>{@code TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G}</li>
      * </ol>
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     @RequiresFeature(
@@ -9734,6 +10080,8 @@
      * @throws IllegalStateException    if the Telephony process is not currently available.
      * @throws IllegalArgumentException if invalid AllowedNetworkTypesReason is passed.
      * @throws SecurityException if the caller does not have the required permission/privileges
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     @RequiresFeature(
@@ -9808,6 +10156,9 @@
      * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @return true on success; false on any failure.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
     public boolean setPreferredNetworkTypeToGlobal() {
@@ -9833,6 +10184,9 @@
      * Requires Permission: MODIFY_PHONE_STATE.
      *
      * @return {@code true} if DUN APN is required for tethering.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -9886,6 +10240,9 @@
      * is a superset of the checks done in SubscriptionManager#canManageSubscription.
      *
      * @return true if the app has carrier privileges.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public boolean hasCarrierPrivileges() {
@@ -9933,6 +10290,9 @@
      *
      * @param brand The brand name to display/set.
      * @return true if the operation was executed correctly.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public boolean setOperatorBrandOverride(String brand) {
@@ -10034,7 +10394,11 @@
      * Expose the rest of ITelephony to @SystemApi
      */
 
-    /** @hide */
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+     * @hide
+     */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
@@ -10042,7 +10406,11 @@
         return getCdmaMdn(getSubId());
     }
 
-    /** @hide */
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+     * @hide
+     */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
@@ -10059,7 +10427,11 @@
         }
     }
 
-    /** @hide */
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+     * @hide
+     */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
@@ -10067,7 +10439,11 @@
         return getCdmaMin(getSubId());
     }
 
-    /** @hide */
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
+     * @hide
+     */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CDMA)
@@ -10084,7 +10460,11 @@
         }
     }
 
-    /** @hide */
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@@ -10101,7 +10481,11 @@
         return CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
     }
 
-    /** @hide */
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@@ -10118,14 +10502,22 @@
         return CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
     }
 
-    /** @hide */
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
     @SystemApi
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public List<String> getCarrierPackageNamesForIntent(Intent intent) {
         return getCarrierPackageNamesForIntentAndPhone(intent, getPhoneId());
     }
 
-    /** @hide */
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@@ -10152,10 +10544,13 @@
      * @return The system-selected package that provides the {@link CarrierService} implementation
      * for the current subscription, or {@code null} if none is resolved
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public @Nullable String getCarrierServicePackageName() {
         return getCarrierServicePackageNameForLogicalSlot(getPhoneId());
     }
@@ -10169,10 +10564,13 @@
      * @return The system-selected package that provides the {@link CarrierService} implementation
      * for the slot, or {@code null} if none is resolved
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public @Nullable String getCarrierServicePackageNameForLogicalSlot(int logicalSlotIndex) {
         try {
             ITelephony telephony = getITelephony();
@@ -10206,6 +10604,8 @@
     /**
      * Get the names of packages with carrier privileges for all the active subscriptions.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -10256,6 +10656,8 @@
      *
      * @throws IllegalArgumentException if requested state is invalid.
      * @throws SecurityException if the caller does not have the permission.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
@@ -10285,6 +10687,9 @@
      *
      * @return the user-set status for enriched calling with call composer, either of
      * {@link #CALL_COMPOSER_STATUS_ON} or {@link #CALL_COMPOSER_STATUS_OFF}.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
@@ -10301,7 +10706,11 @@
         return CALL_COMPOSER_STATUS_OFF;
     }
 
-    /** @hide */
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     * @hide
+     */
     @SystemApi
     @SuppressLint("RequiresPermission")
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
@@ -10316,6 +10725,9 @@
     }
 
     /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     *
      * @deprecated Use  {@link android.telecom.TelecomManager#placeCall(Uri address,
      * Bundle extras)} instead.
      * @hide
@@ -10323,6 +10735,7 @@
     @Deprecated
     @SystemApi
     @RequiresPermission(android.Manifest.permission.CALL_PHONE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
     public void call(String callingPackage, String number) {
         try {
             ITelephony telephony = getITelephony();
@@ -10369,6 +10782,8 @@
     }
 
     /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELECOM}.
      * @deprecated Use {@link android.telecom.TelecomManager#isInCall} instead
      * @hide
      */
@@ -10378,12 +10793,15 @@
             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
             android.Manifest.permission.READ_PHONE_STATE
     })
+    @RequiresFeature(PackageManager.FEATURE_TELECOM)
     public boolean isOffhook() {
         TelecomManager tm = (TelecomManager) mContext.getSystemService(TELECOM_SERVICE);
         return tm.isInCall();
     }
 
     /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELECOM}.
      * @deprecated Use {@link android.telecom.TelecomManager#isRinging} instead
      * @hide
      */
@@ -10393,12 +10811,15 @@
             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
             android.Manifest.permission.READ_PHONE_STATE
     })
+    @RequiresFeature(PackageManager.FEATURE_TELECOM)
     public boolean isRinging() {
         TelecomManager tm = (TelecomManager) mContext.getSystemService(TELECOM_SERVICE);
         return tm.isRinging();
     }
 
     /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELECOM}.
      * @deprecated Use {@link android.telecom.TelecomManager#isInCall} instead
      * @hide
      */
@@ -10408,12 +10829,15 @@
             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
             android.Manifest.permission.READ_PHONE_STATE
     })
+    @RequiresFeature(PackageManager.FEATURE_TELECOM)
     public boolean isIdle() {
         TelecomManager tm = (TelecomManager) mContext.getSystemService(TELECOM_SERVICE);
         return !tm.isInCall();
     }
 
     /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @deprecated Use {@link android.telephony.TelephonyManager#getServiceState} instead
      * @hide
      */
@@ -10423,6 +10847,7 @@
             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
             android.Manifest.permission.READ_PHONE_STATE
     })
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
     public boolean isRadioOn() {
         try {
             ITelephony telephony = getITelephony();
@@ -10434,7 +10859,11 @@
         return false;
     }
 
-    /** @hide */
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@@ -10449,7 +10878,11 @@
         return false;
     }
 
-    /** @hide */
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     * @hide
+     */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@@ -10465,11 +10898,15 @@
     }
 
     /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     *
      * @deprecated use {@link #supplyIccLockPin(String)} instead.
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     @Deprecated
     public int[] supplyPinReportResult(String pin) {
         try {
@@ -10483,11 +10920,15 @@
     }
 
     /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+     *
      * @deprecated use {@link #supplyIccLockPuk(String, String)} instead.
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     @Deprecated
     public int[] supplyPukReportResult(String puk, String pin) {
         try {
@@ -10513,6 +10954,8 @@
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -10549,6 +10992,8 @@
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -10622,6 +11067,9 @@
      * @param callback called by the framework to inform the caller of the result of executing the
      *                 USSD request (see {@link UssdResponseCallback}).
      * @param handler the {@link Handler} to run the request on.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @RequiresPermission(android.Manifest.permission.CALL_PHONE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
@@ -10666,6 +11114,9 @@
      * voice and data simultaneously. This can change based on location or network condition.
      *
      * @return {@code true} if simultaneous voice and data supported, and {@code false} otherwise.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
     public boolean isConcurrentVoiceAndDataSupported() {
@@ -10679,9 +11130,13 @@
         return false;
     }
 
-    /** @hide */
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     * @hide */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
     public boolean handlePinMmi(String dialString) {
         try {
             ITelephony telephony = getITelephony();
@@ -10693,9 +11148,14 @@
         return false;
     }
 
-    /** @hide */
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
+     * @hide
+     */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
     public boolean handlePinMmiForSubscriber(int subId, String dialString) {
         try {
             ITelephony telephony = getITelephony();
@@ -10707,7 +11167,11 @@
         return false;
     }
 
-    /** @hide */
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     * @hide
+     */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
@@ -10725,6 +11189,8 @@
      * @deprecated - use the APIs {@link requestRadioPowerOffForReason} and
      * {@link clearRadioPowerOffForReason}.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @Deprecated
@@ -10752,6 +11218,8 @@
      * @deprecated - use the APIs {@link requestRadioPowerOffForReason} and
      * {@link clearRadioPowerOffForReason}.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @Deprecated
@@ -10799,6 +11267,8 @@
      * @throws SecurityException if the caller does not have MODIFY_PHONE_STATE permission.
      * @throws IllegalStateException if the Telephony service is not currently available.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @SystemApi
@@ -10828,6 +11298,8 @@
      * @throws SecurityException if the caller does not have MODIFY_PHONE_STATE permission.
      * @throws IllegalStateException if the Telephony service is not currently available.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @SystemApi
@@ -10857,6 +11329,8 @@
      * @throws SecurityException if the caller does not have READ_PRIVILEGED_PHONE_STATE permission.
      * @throws IllegalStateException if the Telephony service is not currently available.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @SystemApi
@@ -10886,6 +11360,8 @@
      * <p>To know when the radio has completed powering off, use
      * {@link PhoneStateListener#LISTEN_SERVICE_STATE LISTEN_SERVICE_STATE}.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @SystemApi
@@ -10907,6 +11383,9 @@
      * Check if any radio is on over all the slot indexes.
      *
      * @return {@code true} if any radio is on over any slot index.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @SystemApi
@@ -10953,6 +11432,8 @@
      * {@link android.Manifest.permission#READ_PHONE_STATE} or that the calling
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @SystemApi
@@ -10982,7 +11463,11 @@
         Log.e(TAG, "Do not call TelephonyManager#updateServiceLocation()");
     }
 
-    /** @hide */
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+     * @hide
+     */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
@@ -10997,7 +11482,11 @@
         return false;
     }
 
-    /** @hide */
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+     * @hide
+     */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
@@ -11012,7 +11501,11 @@
         return false;
     }
 
-    /** @hide */
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+     * @hide
+     */
     @SystemApi
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
     public boolean isDataConnectivityPossible() {
@@ -11027,7 +11520,11 @@
         return false;
     }
 
-    /** @hide */
+    /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
+     * @hide
+     */
     @SystemApi
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
     public boolean needsOtaServiceProvisioning() {
@@ -11078,23 +11575,29 @@
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param enable Whether to enable mobile data.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      * @deprecated use setDataEnabledForReason with reason DATA_ENABLED_REASON_USER instead.
      *
      */
     @Deprecated
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
     public void setDataEnabled(boolean enable) {
         setDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enable);
     }
 
     /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      * @hide
      * @deprecated use {@link #setDataEnabledForReason(int, boolean)} instead.
     */
     @SystemApi
     @Deprecated
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
     public void setDataEnabled(int subId, boolean enable) {
         try {
             setDataEnabledForReason(subId, DATA_ENABLED_REASON_USER, enable);
@@ -11105,10 +11608,14 @@
 
     /**
      * @deprecated use {@link #isDataEnabled()} instead.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      * @hide
      */
     @SystemApi
     @Deprecated
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
     public boolean getDataEnabled() {
         return isDataEnabled();
     }
@@ -11132,6 +11639,9 @@
      * {@link ConnectivityManager#getRestrictBackgroundStatus}.
      *
      * @return true if mobile data is enabled.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      */
     @RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE,
             android.Manifest.permission.MODIFY_PHONE_STATE,
@@ -11162,6 +11672,9 @@
      * has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @return {@code true} if the data roaming is enabled on the subscription, otherwise return
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      * {@code false}.
      */
     @RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE,
@@ -11201,6 +11714,8 @@
      * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
      * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
      * @hide
      */
     @SystemApi
@@ -11243,6 +11758,8 @@
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
      * @hide
      */
     @SystemApi
@@ -11311,6 +11828,8 @@
      * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
      * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
      * @hide
      */
     @SystemApi
@@ -11349,6 +11868,8 @@
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
      * @hide
      */
     @SystemApi
@@ -11384,6 +11905,8 @@
      *
      * @param isEnabled {@code true} to enable mobile data roaming, otherwise disable it.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      * @hide
      */
     @SystemApi
@@ -11402,11 +11925,15 @@
     }
 
     /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
+     *
      * @deprecated use {@link #isDataEnabled()} instead.
      * @hide
      */
     @Deprecated
     @SystemApi
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
     public boolean getDataEnabled(int subId) {
         try {
             return isDataEnabledForReason(subId, DATA_ENABLED_REASON_USER);
@@ -11417,6 +11944,8 @@
     }
 
     /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      * @deprecated Use {@link android.telephony.ims.ImsMmTelManager#setVtSettingEnabled(boolean)}
      * instead.
      * @hide
@@ -11424,6 +11953,7 @@
     @Deprecated
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS)
     public void enableVideoCalling(boolean enable) {
         try {
             ITelephony telephony = getITelephony();
@@ -11435,6 +11965,8 @@
     }
 
     /**
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      * @deprecated Use {@link ImsMmTelManager#isVtSettingEnabled()} instead to check if the user
      * has enabled the Video Calling setting, {@link ImsMmTelManager#isAvailable(int, int)} to
      * determine if video calling is available, or {@link ImsMmTelManager#isCapable(int, int)} to
@@ -11447,6 +11979,7 @@
             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
             android.Manifest.permission.READ_PHONE_STATE
     })
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS)
     public boolean isVideoCallingEnabled() {
         try {
             ITelephony telephony = getITelephony();
@@ -11462,6 +11995,9 @@
      * Whether the device supports configuring the DTMF tone length.
      *
      * @return {@code true} if the DTMF tone length can be changed, and {@code false} otherwise.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
     public boolean canChangeDtmfToneLength() {
@@ -11483,7 +12019,11 @@
      * Whether the device is a world phone.
      *
      * @return {@code true} if the device is a world phone, and {@code false} otherwise.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY}.
      */
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
     public boolean isWorldPhone() {
         try {
             ITelephony telephony = getITelephony();
@@ -11504,8 +12044,11 @@
      *
      * @return {@code true} if the device supports TTY mode, and {@code false} otherwise.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELECOM}.
      */
     @Deprecated
+    @RequiresFeature(PackageManager.FEATURE_TELECOM)
     public boolean isTtyModeSupported() {
         try {
             TelecomManager telecomManager = null;
@@ -11526,6 +12069,9 @@
      * support for the feature and device firmware support.
      *
      * @return {@code true} if the device and carrier both support RTT, {@code false} otherwise.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_IMS}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS)
     public boolean isRttSupported() {
@@ -11546,6 +12092,9 @@
      *
      * @return {@code true} if the device supports hearing aid compatibility, and {@code false}
      * otherwise.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
     public boolean isHearingAidCompatibilitySupported() {
@@ -11808,11 +12357,14 @@
      * <p>Requires Permission:
      *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * {@hide}
      **/
     @SystemApi
     @Deprecated
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public void setSimPowerState(int state) {
         setSimPowerStateForSlot(getSlotIndex(), state);
     }
@@ -11834,11 +12386,14 @@
      * <p>Requires Permission:
      *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * {@hide}
      **/
     @SystemApi
     @Deprecated
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public void setSimPowerStateForSlot(int slotIndex, int state) {
         try {
             ITelephony telephony = getITelephony();
@@ -11871,6 +12426,8 @@
      * <p>Requires Permission:
      *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * {@hide}
      **/
     @SystemApi
@@ -11901,6 +12458,8 @@
      * <p>Requires Permission:
      *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * {@hide}
      **/
     @SystemApi
@@ -12055,6 +12614,9 @@
      * application currently configured for this user.
      * @return component name of the app and class to direct Respond Via Message intent to, or
      * {@code null} if the functionality is not supported.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      * @hide
      */
     @SystemApi
@@ -12077,6 +12639,9 @@
      * user associated with this subscription.
      * @return component name of the app and class to direct Respond Via Message intent to, or
      * {@code null} if the functionality is not supported.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      * @hide
      */
     @SystemApi
@@ -12213,9 +12778,13 @@
      * @return The {@link PhoneAccountHandle} associated with the TelphonyManager, or {@code null}
      * if there is no associated {@link PhoneAccountHandle}; this can happen if the subscription is
      * data-only or an opportunistic subscription.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
     public @Nullable PhoneAccountHandle getPhoneAccountHandle() {
         return getPhoneAccountHandleForSubscriptionId(getSubId());
     }
@@ -12277,10 +12846,14 @@
      * Resets Telephony and IMS settings back to factory defaults only for the subscription
      * associated with this instance.
      * @see #createForSubscriptionId(int)
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY}.
      * @hide
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.CONNECTIVITY_INTERNAL)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
     public void resetSettings() {
         try {
             Log.d(TAG, "resetSettings: subId=" + getSubId());
@@ -12302,6 +12875,8 @@
      *
      * @see Locale#toLanguageTag()
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -12394,10 +12969,14 @@
      * @param callback A callback object to which the result will be delivered. If there was an
      *                 error processing the request, {@link OutcomeReceiver#onError} will be called
      *                 with more details about the error.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY}.
      * @hide
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
     public void requestModemActivityInfo(@NonNull @CallbackExecutor Executor executor,
             @NonNull OutcomeReceiver<ModemActivityInfo, ModemActivityInfoException> callback) {
         Objects.requireNonNull(executor);
@@ -12484,6 +13063,9 @@
      * and {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
      * May return {@code null} when the subscription is inactive or when there was an error
      * communicating with the phone process.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(allOf = {
@@ -12516,12 +13098,16 @@
      * location related information.
      * May return {@code null} when the subscription is inactive or when there was an error
      * communicating with the phone process.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(allOf = {
             Manifest.permission.READ_PHONE_STATE,
             Manifest.permission.ACCESS_COARSE_LOCATION
     })
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
     public @Nullable ServiceState getServiceState(@IncludeLocationData int includeLocationData) {
         return getServiceStateForSubscriber(getSubId(),
                 includeLocationData != INCLUDE_LOCATION_DATA_FINE,
@@ -12580,6 +13166,9 @@
      * voicemail ringtone.
      * @return The URI for the ringtone to play when receiving a voicemail from a specific
      * PhoneAccount. May be {@code null} if no ringtone is set.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
     public @Nullable Uri getVoicemailRingtoneUri(PhoneAccountHandle accountHandle) {
@@ -12606,10 +13195,13 @@
      * @param uri The URI for the ringtone to play when receiving a voicemail from a specific
      * PhoneAccount.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      * @deprecated Use {@link android.provider.Settings#ACTION_CHANNEL_NOTIFICATION_SETTINGS}
      * instead.
      */
     @Deprecated
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
     public void setVoicemailRingtoneUri(PhoneAccountHandle phoneAccountHandle, Uri uri) {
         try {
             ITelephony service = getITelephony();
@@ -12627,6 +13219,9 @@
      * @param accountHandle The handle for the {@link PhoneAccount} for which to retrieve the
      * voicemail vibration setting.
      * @return {@code true} if the vibration is set for this PhoneAccount, {@code false} otherwise.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
     public boolean isVoicemailVibrationEnabled(PhoneAccountHandle accountHandle) {
@@ -12653,10 +13248,13 @@
      * @param enabled Whether to enable or disable vibration for voicemail notifications from a
      * specific PhoneAccount.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      * @deprecated Use {@link android.provider.Settings#ACTION_CHANNEL_NOTIFICATION_SETTINGS}
      * instead.
      */
     @Deprecated
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
     public void setVoicemailVibrationEnabled(PhoneAccountHandle phoneAccountHandle,
             boolean enabled) {
         try {
@@ -12682,6 +13280,9 @@
      *
      * @return Carrier id of the current subscription. Return {@link #UNKNOWN_CARRIER_ID} if the
      * subscription is unavailable or the carrier cannot be identified.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public int getSimCarrierId() {
@@ -12707,6 +13308,9 @@
      *
      * @return Carrier name of the current subscription. Return {@code null} if the subscription is
      * unavailable or the carrier cannot be identified.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public @Nullable CharSequence getSimCarrierIdName() {
@@ -12745,6 +13349,9 @@
      * @return Returns fine-grained carrier id of the current subscription.
      * Return {@link #UNKNOWN_CARRIER_ID} if the subscription is unavailable or the carrier cannot
      * be identified.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public int getSimSpecificCarrierId() {
@@ -12771,6 +13378,9 @@
      *
      * @return user-facing name of the subscription specific carrier id. Return {@code null} if the
      * subscription is unavailable or the carrier cannot be identified.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public @Nullable CharSequence getSimSpecificCarrierIdName() {
@@ -12799,6 +13409,9 @@
      *
      * @return matching carrier id from sim MCCMNC. Return {@link #UNKNOWN_CARRIER_ID} if the
      * subscription is unavailable or the carrier cannot be identified.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public int getCarrierIdFromSimMccMnc() {
@@ -12874,6 +13487,9 @@
      *
      * @param appType the uicc app type.
      * @return Application ID for specified app type or {@code null} if no uicc or error.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @Nullable
@@ -12939,6 +13555,9 @@
      * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission
      *
      * @return PRLVersion or null if error.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CDMA}.
      * @hide
      */
     @SystemApi
@@ -13004,6 +13623,9 @@
      *
      * @return The number of carriers set successfully. Should be length of
      * carrierList on success; -1 if carrierList null or on error.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CARRIERLOCK}.
      * @hide
      */
     @SystemApi
@@ -13130,6 +13752,9 @@
      * @return {@link #SET_CARRIER_RESTRICTION_SUCCESS} in case of success.
      * {@link #SET_CARRIER_RESTRICTION_NOT_SUPPORTED} if the modem does not support the
      * configuration. {@link #SET_CARRIER_RESTRICTION_ERROR} in all other error cases.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CARRIERLOCK}.
      * @hide
      */
     @SystemApi
@@ -13163,11 +13788,15 @@
      *
      * @return List of {@link android.telephony.CarrierIdentifier}; empty list
      * means all carriers are allowed.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CARRIERLOCK}.
      * @hide
      */
     @Deprecated
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CARRIERLOCK)
     public List<CarrierIdentifier> getAllowedCarriers(int slotIndex) {
         if (SubscriptionManager.isValidPhoneId(slotIndex)) {
             CarrierRestrictionRules carrierRestrictionRule = getCarrierRestrictionRules();
@@ -13189,6 +13818,9 @@
      * @return {@link CarrierRestrictionRules} which contains the allowed carrier list and the
      * excluded carrier list with the priority between the two lists. Returns {@code null}
      * in case of error.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CARRIERLOCK}.
      * @hide
      */
     @SystemApi
@@ -13257,6 +13889,8 @@
      *                       status result fetched from the radio
      * @throws SecurityException if the caller does not have the required permission/privileges or
      *                           if the caller is not pre-registered.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@@ -13323,11 +13957,15 @@
      * @see #resetAllCarrierActions()
      * @deprecated use {@link #setDataEnabledForReason(int, boolean) with
      * reason {@link #DATA_ENABLED_REASON_CARRIER}} instead.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      * @hide
      */
     @Deprecated
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
     public void setCarrierDataEnabled(boolean enabled) {
         try {
             setDataEnabledForReason(DATA_ENABLED_REASON_CARRIER, enabled);
@@ -13351,6 +13989,8 @@
      * @deprecated - use the APIs {@link requestRadioPowerOffForReason} and
      * {@link clearRadioPowerOffForReason}.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @Deprecated
@@ -13470,6 +14110,9 @@
      *
      * @param report control start/stop reporting network status.
      * @see #resetAllCarrierActions()
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @SystemApi
@@ -13496,6 +14139,8 @@
      *
      * <p>Requires Permission:
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -13618,6 +14263,8 @@
      * has {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} irrespective of
      * the reason.
      * @throws IllegalStateException if the Telephony process is not currently available.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
@@ -13661,6 +14308,8 @@
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE}
      * {@link android.Manifest.permission#READ_BASIC_PHONE_STATE}
      * @throws IllegalStateException if the Telephony process is not currently available.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      */
     @RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE,
             android.Manifest.permission.READ_PHONE_STATE,
@@ -13717,6 +14366,9 @@
      * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
      *
      * @return true if phone is in emergency callback mode.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      * @hide
      */
     @SystemApi
@@ -13756,6 +14408,9 @@
      * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}.
      *
      * @return {@code true} if manual network selection is allowed, otherwise return {@code false}.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @SuppressAutoDoc // No support carrier privileges (b/72967236).
     @RequiresPermission(anyOf = {android.Manifest.permission.READ_PRECISE_PHONE_STATE,
@@ -13779,6 +14434,9 @@
      * Get the most recent SignalStrength information reported by the modem. Due
      * to power saving this information may not always be current.
      * @return the most recent cached signal strength info from the modem
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @Nullable
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
@@ -13805,6 +14463,9 @@
      *   <LI>And possibly others.</LI>
      * </UL>
      * @return {@code true} if the overall data connection is allowed; {@code false} if not.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      */
     @RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE,
             android.Manifest.permission.READ_PHONE_STATE,
@@ -13975,6 +14636,9 @@
      *
      * @param enable enable(True) or disable(False)
      * @return returns true if successfully set.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -14004,6 +14668,9 @@
      * <p>
      * Requires Permission:
      *   {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@@ -14205,6 +14872,8 @@
      * that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @throws SecurityException if the caller does not have the required permission
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
@@ -14240,6 +14909,8 @@
      * <p> Requires permission:
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -14267,6 +14938,8 @@
      * <p> Requires permission:
      * {@link android.Manifest.permission#READ_ACTIVE_EMERGENCY_SESSION}
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
@@ -14293,6 +14966,8 @@
      * <p> Requires permission:
      * {@link android.Manifest.permission#READ_ACTIVE_EMERGENCY_SESSION}
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
@@ -14353,6 +15028,9 @@
      * subscription, the key is {@link SubscriptionManager#getDefaultSubscriptionId}) and the value
      * as the list of {@link EmergencyNumber}; empty Map if this information is not available;
      * or throw a SecurityException if the caller does not have the permission.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     @NonNull
@@ -14409,6 +15087,8 @@
      * as the list of {@link EmergencyNumber}; empty Map if this information is not available;
      * or throw a SecurityException if the caller does not have the permission.
      * @throws IllegalStateException if the Telephony process is not currently available.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     @NonNull
@@ -14477,6 +15157,8 @@
      * @return {@code true} if the given number is an emergency number based on current locale,
      * SIM card(s), Android database, modem, network or defaults; {@code false} otherwise.
      * @throws IllegalStateException if the Telephony process is not currently available.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
     public boolean isEmergencyNumber(@NonNull String number) {
@@ -14514,6 +15196,8 @@
      * network; {@code false} if it is not; or throw an SecurityException if the caller does not
      * have the required permission/privileges
      * @throws IllegalStateException if the Telephony process is not currently available.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      *
      * @deprecated Please use {@link TelephonyManager#isEmergencyNumber(String)} instead.
      * @hide
@@ -14543,6 +15227,8 @@
      * <p>Requires Permission:
      *   {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      * @hide
      */
     @SystemApi
@@ -14694,6 +15380,9 @@
      * @param callback Callback will be triggered once it succeeds or failed.
      *                 See the {@code SET_OPPORTUNISTIC_SUB_*} constants
      *                 for more details. Pass null if don't care about the result.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
     public void setPreferredOpportunisticDataSubscription(int subId, boolean needValidation,
@@ -14754,6 +15443,8 @@
      * {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID} if there are no preferred
      * subscription id
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      */
     @RequiresPermission(anyOf = {
             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
@@ -14793,6 +15484,8 @@
      * @param executor The executor of where the callback will execute.
      * @param callback Callback will be triggered once it succeeds or failed.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
@@ -14853,10 +15546,13 @@
      * @param enable whether to enable or disable the modem stack.
      * @return whether the operation is successful.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY}.
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
     public boolean enableModemForSlot(int slotIndex, boolean enable) {
         boolean ret = false;
         try {
@@ -14879,10 +15575,14 @@
      * {@link #hasCarrierPrivileges()}).
      *
      * @param slotIndex which slot it's checking.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(anyOf = {android.Manifest.permission.READ_PHONE_STATE,
             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE})
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY)
     public boolean isModemEnabledForSlot(int slotIndex) {
         try {
             ITelephony telephony = getITelephony();
@@ -14945,6 +15645,8 @@
      * @param isMultiSimCarrierRestricted true if usage of multiple SIMs is restricted, false
      * otherwise.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CARRIERLOCK}.
      * @hide
      */
     @SystemApi
@@ -15000,6 +15702,9 @@
      * {@link #MULTISIM_NOT_SUPPORTED_BY_HARDWARE} if the device does not support multiple SIMs.
      * {@link #MULTISIM_NOT_SUPPORTED_BY_CARRIER} in the device supports multiple SIMs, but the
      * functionality is restricted by the carrier.
+     *
+     * @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)
@@ -15031,6 +15736,8 @@
      *
      * @param numOfSims number of live SIMs we want to switch to
      * @throws android.os.RemoteException
+     * @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)
@@ -15058,6 +15765,9 @@
      *
      * @return {@code true} if reboot will be triggered after making changes to modem
      * configurations, otherwise return {@code false}.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@@ -15220,6 +15930,8 @@
      *         {@link #CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED}, or
      *         {@link #CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES}
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -15334,6 +16046,8 @@
      * @param apnType Value indicating the apn type. Apn types are defined in {@link ApnSetting}.
      * @return whether data is enabled for a apn type.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      * @hide
      */
     @SystemApi
@@ -15356,6 +16070,8 @@
      * Whether an APN type is metered or not. It will be evaluated with the subId associated
      * with the TelephonyManager instance.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      * @hide
      */
     @SystemApi
@@ -15385,6 +16101,9 @@
      * @param executor The executor to execute the callback on
      * @param callback The callback that gets invoked when the radio responds to the request. Called
      *                 with {@code true} if the request succeeded, {@code false} otherwise.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @SystemApi
@@ -15403,6 +16122,9 @@
      * Same as {@link #setSystemSelectionChannels(List, Executor, Consumer<Boolean>)}, but to be
      * used when the caller does not need feedback on the results of the operation.
      * @param specifiers which bands to scan.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @SystemApi
@@ -15450,6 +16172,8 @@
      * @return a list of {@link RadioAccessSpecifier}, or an empty list if no bands are specified.
      * @throws IllegalStateException if the Telephony process is not currently available.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @SystemApi
@@ -15478,6 +16202,8 @@
      * @return {@code true} if input mccmnc and mvno matches with data from sim operator.
      * {@code false} otherwise.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * {@hide}
      */
     @SystemApi
@@ -15568,6 +16294,8 @@
      * {@link CallForwardingInfo#REASON_UNCONDITIONAL}, {@link CallForwardingInfo#REASON_BUSY},
      * {@link CallForwardingInfo#REASON_NO_REPLY}, {@link CallForwardingInfo#REASON_NOT_REACHABLE},
      * {@link CallForwardingInfo#REASON_ALL}, or {@link CallForwardingInfo#REASON_ALL_CONDITIONAL}
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      *
      * @hide
      */
@@ -15645,6 +16373,8 @@
      * <li>{@link CallForwardingInfo#getTimeoutSeconds()} returns a non-positive value when
      * enabling call forwarding</li>
      * </ul>
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -15769,6 +16499,9 @@
      *                          <li>{@link #CALL_WAITING_STATUS_NOT_SUPPORTED}}</li>
      *                          <li>{@link #CALL_WAITING_STATUS_FDN_CHECK_FAILURE}}</li>
      *                       </ul>
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      * @hide
      */
     @SystemApi
@@ -15819,6 +16552,9 @@
      *                       {@link #CALL_WAITING_STATUS_NOT_SUPPORTED} or
      *                       {@link #CALL_WAITING_STATUS_UNKNOWN_ERROR} or
      *                       {@link #CALL_WAITING_STATUS_FDN_CHECK_FAILURE} if it failed.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_CALLING}.
      * @hide
      */
     @SystemApi
@@ -15919,6 +16655,9 @@
      *
      * @param policy The data policy to enable.
      * @param enabled Whether to enable or disable the policy.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      * @hide
      */
     @SystemApi
@@ -15940,6 +16679,9 @@
      *
      * @param policy The data policy that you want the status for.
      * @return {@code true} if enabled, {@code false} otherwise.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      * @hide
      */
     @SystemApi
@@ -15975,6 +16717,8 @@
      * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
      * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @WorkerThread
@@ -16009,6 +16753,8 @@
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -16051,6 +16797,8 @@
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -16199,6 +16947,8 @@
      * </ol>
      * @return operation result.
      * @throws IllegalStateException if the Telephony process is not currently available.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @SystemApi
@@ -16233,6 +16983,8 @@
      * connectivity is active. It means the device is allowed to connect to both primary and
      * secondary cell.
      * @throws IllegalStateException if the Telephony process is not currently available.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @SystemApi
@@ -16470,6 +17222,8 @@
      *
      * @throws IllegalStateException if the Telephony process is not currently available.
      * @throws SecurityException if the caller doesn't have the permission.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      *
      */
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@@ -16586,6 +17340,9 @@
      *
      * @param capability the name of the capability to check for
      * @return the availability of the capability
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
     public boolean isRadioInterfaceCapabilitySupported(
@@ -16705,6 +17462,8 @@
      * @throws IllegalArgumentException if the thermalMitigationRequest had invalid parameters or
      * if the device's modem does not support data throttling.
      *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      * @hide
      */
     @SystemApi
@@ -17037,6 +17796,9 @@
      * contain the GBA Ks_NAF/Ks_ext_NAF when available. If the NAF keys are
      * available and valid at the time of call and bootstrapping is not requested,
      * then the callback shall be invoked with the available keys.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
@@ -17135,6 +17897,8 @@
      * @param request the SignalStrengthUpdateRequest to be set into the System
      *
      * @throws IllegalStateException if a new request is set with same subId from the same caller
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -17165,6 +17929,9 @@
      * @see #setSignalStrengthUpdateRequest(SignalStrengthUpdateRequest)
      *
      * @param request the SignalStrengthUpdateRequest to be cleared from the System
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -17188,10 +17955,14 @@
      * @return the PhoneCapability which describes the data connection capability of modem.
      * It's used to evaluate possible phone config change, for example from single
      * SIM device to multi-SIM device.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public @NonNull PhoneCapability getPhoneCapability() {
         try {
             ITelephony telephony = getITelephony();
@@ -17256,11 +18027,15 @@
      * at least one SIM card for which the user needs to manually enter the PIN
      * code after the reboot. {@link #PREPARE_UNATTENDED_REBOOT_ERROR} in case
      * of error.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.REBOOT)
     @PrepareUnattendedRebootResult
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
     public int prepareForUnattendedReboot() {
         try {
             ITelephony service = getITelephony();
@@ -17363,6 +18138,9 @@
      *
      * @param executor the executor on which callback will be invoked.
      * @param callback a callback to receive the current slicing configuration.
+     *
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_RADIO_ACCESS}.
      */
     @RequiresFeature(
             enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
@@ -17444,8 +18222,11 @@
      * @param capability The premium capability to check.
      * @return Whether the given premium capability is available to purchase.
      * @throws SecurityException if the caller does not hold permission READ_BASIC_PHONE_STATE.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      */
     @RequiresPermission(android.Manifest.permission.READ_BASIC_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
     public boolean isPremiumCapabilityAvailableForPurchase(@PremiumCapability int capability) {
         try {
             ITelephony telephony = getITelephony();
@@ -17685,10 +18466,13 @@
      * @param callback The result of the purchase request.
      * @throws SecurityException if the caller does not hold permissions
      *         READ_BASIC_PHONE_STATE or INTERNET.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_DATA}.
      * @see #isPremiumCapabilityAvailableForPurchase(int) to check whether the capability is valid.
      */
     @RequiresPermission(allOf = {android.Manifest.permission.READ_BASIC_PHONE_STATE,
             android.Manifest.permission.INTERNET})
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
     public void purchasePremiumCapability(@PremiumCapability int capability,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull @PurchasePremiumCapabilityResult Consumer<Integer> callback) {
@@ -18142,10 +18926,14 @@
      * Get current cell broadcast message identifier ranges.
      *
      * @throws SecurityException if the caller does not have the required permission
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
+     *
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     @NonNull
     public List<CellBroadcastIdRange> getCellBroadcastIdRanges() {
         try {
@@ -18299,10 +19087,13 @@
      * the result when the operation completes.
      * @throws SecurityException if the caller does not have the required permission
      * @throws IllegalArgumentException when the ranges are invalid.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_MESSAGING}.
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     public void setCellBroadcastIdRanges(@NonNull List<CellBroadcastIdRange> ranges,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull Consumer<Integer> callback) {
@@ -18378,7 +19169,8 @@
      * </ul>
      *
      * @return Primary IMEI of type string
-     * @throws UnsupportedOperationException if the radio doesn't support this feature.
+     * @throws UnsupportedOperationException If the device does not have
+     *          {@link PackageManager#FEATURE_TELEPHONY_GSM}.
      * @throws SecurityException if the caller does not have the required permission/privileges
      */
     @NonNull